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"
8 
9 namespace Svc {
10 
11 void FpySequencer::sendSignal(Signal signal) {
12  switch (signal) {
15  break;
16  }
19  break;
20  }
23  break;
24  }
27  break;
28  }
29  default: {
30  FW_ASSERT(false, static_cast<FwAssertArgType>(signal));
31  }
32  }
33 }
34 
35 // utility method for updating telemetry based on a directive error code
36 void FpySequencer::handleDirectiveErrorCode(Fpy::DirectiveId id, DirectiveError err) {
37  this->m_tlm.lastDirectiveError = err;
38  if (err != DirectiveError::NO_ERROR) {
39  this->m_tlm.directiveErrorIndex = this->currentStatementIdx();
40  this->m_tlm.directiveErrorId = id;
41  }
42 }
43 
44 Fw::Success FpySequencer::sendCmd(FwOpcodeType opcode, const U8* argBuf, FwSizeType argBufSize) {
45  Fw::ComBuffer cmdBuf;
46  Fw::SerializeStatus stat =
47  cmdBuf.serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_COMMAND));
48  // TODO should I assert here? this really shouldn't fail, I should just add a static assert
49  // on com buf size and then assert here
51  return Fw::Success::FAILURE;
52  }
53  stat = cmdBuf.serializeFrom(opcode);
55  return Fw::Success::FAILURE;
56  }
57  stat = cmdBuf.serializeFrom(argBuf, argBufSize, Fw::Serialization::OMIT_LENGTH);
59  return Fw::Success::FAILURE;
60  }
61 
62  // calculate the unique command identifier:
63  // cmd UID is formatted like XXYY, where XX are the first two bytes of the m_sequencesStarted counter
64  // and YY are the first two bytes of the m_statementsDispatched counter.
65  // this way, we know when we get a cmd back A) whether or not it's from this sequence (modulo 2^16) and B)
66  // whether or not it's this specific instance of the cmd in the sequence, and not another one with the same opcode
67  // somewhere else in the file.
68  // if we put this uid in the context we send to the cmdDisp, we will get it back when the cmd returns
69  U32 cmdUid =
70  static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_statementsDispatched & 0xFFFF));
71 
72  this->cmdOut_out(0, cmdBuf, cmdUid);
73 
74  return Fw::Success::SUCCESS;
75 }
76 
80  this->sendSignal(this->waitRel_directiveHandler(directive, error));
81  handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_REL, error);
82 }
83 
87  this->sendSignal(this->waitAbs_directiveHandler(directive, error));
88  handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_ABS, error);
89 }
90 
94  this->sendSignal(this->goto_directiveHandler(directive, error));
95  handleDirectiveErrorCode(Fpy::DirectiveId::GOTO, error);
96 }
97 
101  this->sendSignal(this->if_directiveHandler(directive, error));
102  handleDirectiveErrorCode(Fpy::DirectiveId::IF, error);
103 }
104 
108  this->sendSignal(this->noOp_directiveHandler(directive, error));
109  handleDirectiveErrorCode(Fpy::DirectiveId::NO_OP, error);
110 }
111 
114  const Svc::FpySequencer_PushTlmValDirective& directive) {
116  this->sendSignal(this->pushTlmVal_directiveHandler(directive, error));
117  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL, error);
118 }
119 
124  this->sendSignal(this->pushTlmValAndTime_directiveHandler(directive, error));
125  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL_AND_TIME, error);
126 }
127 
131  this->sendSignal(this->pushPrm_directiveHandler(directive, error));
132  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_PRM, error);
133 }
134 
138  this->sendSignal(this->constCmd_directiveHandler(directive, error));
139  handleDirectiveErrorCode(Fpy::DirectiveId::CONST_CMD, error);
140 }
141 
145  this->sendSignal(this->stackOp_directiveHandler(directive, error));
146  handleDirectiveErrorCode(directive.get__op(), error);
147 }
148 
152  this->sendSignal(this->exit_directiveHandler(directive, error));
153  handleDirectiveErrorCode(Fpy::DirectiveId::EXIT, error);
154 }
155 
159  this->sendSignal(this->allocate_directiveHandler(directive, error));
160  handleDirectiveErrorCode(Fpy::DirectiveId::ALLOCATE, error);
161 }
162 
167  this->sendSignal(this->storeRelConstOffset_directiveHandler(directive, error));
168  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL_CONST_OFFSET, error);
169 }
170 
174  this->sendSignal(this->pushVal_directiveHandler(directive, error));
175  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_VAL, error);
176 }
177 
181  this->sendSignal(this->loadRel_directiveHandler(directive, error));
182  handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_REL, error);
183 }
184 
188  this->sendSignal(this->discard_directiveHandler(directive, error));
189  handleDirectiveErrorCode(Fpy::DirectiveId::DISCARD, error);
190 }
191 
195  this->sendSignal(this->memCmp_directiveHandler(directive, error));
196  handleDirectiveErrorCode(Fpy::DirectiveId::MEMCMP, error);
197 }
198 
202  this->sendSignal(this->stackCmd_directiveHandler(directive, error));
203  handleDirectiveErrorCode(Fpy::DirectiveId::STACK_CMD, error);
204 }
205 
209  this->sendSignal(this->pushTime_directiveHandler(directive, error));
210  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TIME, error);
211 }
212 
216  this->sendSignal(this->setSeed_directiveHandler(directive, error));
217  handleDirectiveErrorCode(Fpy::DirectiveId::SET_SEED, error);
218 }
219 
223  this->sendSignal(this->pushRand_directiveHandler(directive, error));
224  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_RAND, error);
225 }
226 
230  this->sendSignal(this->getField_directiveHandler(directive, error));
231  handleDirectiveErrorCode(Fpy::DirectiveId::GET_FIELD, error);
232 }
233 
237  this->sendSignal(this->peek_directiveHandler(directive, error));
238  handleDirectiveErrorCode(Fpy::DirectiveId::PEEK, error);
239 }
240 
244  this->sendSignal(this->storeRel_directiveHandler(directive, error));
245  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL, error);
246 }
247 
251  this->sendSignal(this->call_directiveHandler(directive, error));
252  handleDirectiveErrorCode(Fpy::DirectiveId::CALL, error);
253 }
254 
258  this->sendSignal(this->return_directiveHandler(directive, error));
259  handleDirectiveErrorCode(Fpy::DirectiveId::RETURN, error);
260 }
261 
265  this->sendSignal(this->loadAbs_directiveHandler(directive, error));
266  handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_ABS, error);
267 }
268 
272  this->sendSignal(this->storeAbs_directiveHandler(directive, error));
273  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS, error);
274 }
275 
280  this->sendSignal(this->storeAbsConstOffset_directiveHandler(directive, error));
281  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS_CONST_OFFSET, error);
282 }
283 
287  this->sendSignal(this->popEvent_directiveHandler(directive, error));
288  handleDirectiveErrorCode(Fpy::DirectiveId::POP_EVENT, error);
289 }
290 
295  this->sendSignal(this->popSerializable_directiveHandler(directive, error));
296  handleDirectiveErrorCode(Fpy::DirectiveId::POP_SERIALIZABLE, error);
297 }
298 
300 Signal FpySequencer::waitRel_directiveHandler(const FpySequencer_WaitRelDirective& directive, DirectiveError& error) {
301  if (this->m_runtime.stack.size < 8) {
304  }
305 
306  Fw::Time wakeupTime = this->getTime();
307 
308  U32 uSeconds = this->m_runtime.stack.pop<U32>();
309  U32 seconds = this->m_runtime.stack.pop<U32>();
310 
311  if (uSeconds >= 1000000) {
314  }
315 
316  wakeupTime.add(seconds, uSeconds);
317  this->m_runtime.wakeupTime = wakeupTime;
319 }
320 
322 Signal FpySequencer::waitAbs_directiveHandler(const FpySequencer_WaitAbsDirective& directive, DirectiveError& error) {
323  if (this->m_runtime.stack.size < 2 * sizeof(U32) + sizeof(FwTimeContextStoreType) + sizeof(FwTimeBaseStoreType)) {
326  }
327 
328  U32 uSeconds = this->m_runtime.stack.pop<U32>();
329  U32 seconds = this->m_runtime.stack.pop<U32>();
330  FwTimeContextStoreType ctx = this->m_runtime.stack.pop<FwTimeContextStoreType>();
331  FwTimeBaseStoreType base = this->m_runtime.stack.pop<FwTimeBaseStoreType>();
332 
333  if (uSeconds >= 1000000) {
336  }
337 
338  this->m_runtime.wakeupTime = Fw::Time(static_cast<TimeBase::T>(base), ctx, seconds, uSeconds);
340 }
341 
343 Signal FpySequencer::goto_directiveHandler(const FpySequencer_GotoDirective& directive, DirectiveError& error) {
344  // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
345  if (directive.get_statementIndex() > m_sequenceObj.get_header().get_statementCount()) {
348  }
349  m_runtime.nextStatementIndex = directive.get_statementIndex();
351 }
352 
354 Signal FpySequencer::if_directiveHandler(const FpySequencer_IfDirective& directive, DirectiveError& error) {
355  if (this->m_runtime.stack.size < 1) {
358  }
359  // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
360  if (directive.get_falseGotoStmtIndex() > m_sequenceObj.get_header().get_statementCount()) {
363  }
364 
365  if (this->m_runtime.stack.pop<U8>() != 0) {
366  // proceed to next instruction
368  }
369 
370  // conditional false case
371  this->m_runtime.nextStatementIndex = directive.get_falseGotoStmtIndex();
373 }
374 
375 Signal FpySequencer::noOp_directiveHandler(const FpySequencer_NoOpDirective& directive, DirectiveError& error) {
377 }
378 
379 Signal FpySequencer::pushTlmVal_directiveHandler(const FpySequencer_PushTlmValDirective& directive,
380  DirectiveError& error) {
381  if (!this->isConnected_getTlmChan_OutputPort(0)) {
384  }
385  Fw::Time tlmTime;
386  Fw::TlmBuffer tlmValue;
387  Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
388 
389  if (valid != Fw::TlmValid::VALID) {
390  // could not find this tlm chan
393  }
394 
395  if (tlmValue.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
398  }
399  this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
401 }
402 
403 Signal FpySequencer::pushTlmValAndTime_directiveHandler(const FpySequencer_PushTlmValAndTimeDirective& directive,
404  DirectiveError& error) {
405  if (!this->isConnected_getTlmChan_OutputPort(0)) {
408  }
409 
410  Fw::Time tlmTime;
411  Fw::TlmBuffer tlmValue;
412  Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
413 
414  if (valid != Fw::TlmValid::VALID) {
415  // could not find this tlm chan
418  }
419 
420  U8 tlmTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
422  Fw::SerializeStatus stat = timeEsb.serializeFrom(tlmTime);
423 
424  // coding error if this failed, we should have enough space
425  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
426 
427  // check that our stack won't overflow if we put both val and time on it
428  if (tlmValue.getSize() + timeEsb.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
431  }
432 
433  // push tlm to end of stack
434  this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
435  // now push time to end of stack
436  this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
438 }
439 
440 Signal FpySequencer::pushPrm_directiveHandler(const FpySequencer_PushPrmDirective& directive, DirectiveError& error) {
441  if (!this->isConnected_getParam_OutputPort(0)) {
444  }
445 
446  Fw::ParamBuffer prmValue;
447  Fw::ParamValid valid = this->getParam_out(0, directive.get_prmId(), prmValue);
448 
449  if (valid != Fw::ParamValid::VALID) {
450  // could not find this prm in the DB
453  }
454 
455  if (prmValue.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
458  }
459 
460  this->m_runtime.stack.push(prmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(prmValue.getSize()));
462 }
463 
464 Signal FpySequencer::constCmd_directiveHandler(const FpySequencer_ConstCmdDirective& directive, DirectiveError& error) {
465  // the cmd response code will be pushed to the stack when it comes back, so make sure
466  // there is room for it now, before the cmd is dispatched
467  if (sizeof(Fw::CmdResponse::SerialType) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
470  }
471  if (this->sendCmd(directive.get_opCode(), directive.get_argBuf(), directive.get__argBufSize()) ==
474  } else {
475  // now tell the SM to wait some more until we get the cmd response back
476  // if we've already got the response back this should be harmless
478  }
479 }
480 
481 DirectiveError FpySequencer::op_or() {
482  if (this->m_runtime.stack.size < sizeof(U8) * 2) {
484  }
485  this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() | this->m_runtime.stack.pop<U8>()));
487 }
488 DirectiveError FpySequencer::op_and() {
489  if (this->m_runtime.stack.size < sizeof(U8) * 2) {
491  }
492  this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() & this->m_runtime.stack.pop<U8>()));
494 }
495 DirectiveError FpySequencer::op_ieq() {
496  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
498  }
499  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() == this->m_runtime.stack.pop<I64>())
500  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
501  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
503 }
504 DirectiveError FpySequencer::op_ine() {
505  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
507  }
508  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() != this->m_runtime.stack.pop<I64>())
509  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
510  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
512 }
513 DirectiveError FpySequencer::op_ult() {
514  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
516  }
517  U64 rhs = this->m_runtime.stack.pop<U64>();
518  U64 lhs = this->m_runtime.stack.pop<U64>();
519  this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
520  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
522 }
523 DirectiveError FpySequencer::op_ule() {
524  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
526  }
527  U64 rhs = this->m_runtime.stack.pop<U64>();
528  U64 lhs = this->m_runtime.stack.pop<U64>();
529  this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
530  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
532 }
533 DirectiveError FpySequencer::op_ugt() {
534  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
536  }
537  U64 rhs = this->m_runtime.stack.pop<U64>();
538  U64 lhs = this->m_runtime.stack.pop<U64>();
539  this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
540  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
542 }
543 DirectiveError FpySequencer::op_uge() {
544  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
546  }
547  U64 rhs = this->m_runtime.stack.pop<U64>();
548  U64 lhs = this->m_runtime.stack.pop<U64>();
549  this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
550  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
552 }
553 DirectiveError FpySequencer::op_slt() {
554  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
556  }
557  I64 rhs = this->m_runtime.stack.pop<I64>();
558  I64 lhs = this->m_runtime.stack.pop<I64>();
559  this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
560  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
562 }
563 DirectiveError FpySequencer::op_sle() {
564  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
566  }
567  I64 rhs = this->m_runtime.stack.pop<I64>();
568  I64 lhs = this->m_runtime.stack.pop<I64>();
569  this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
570  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
572 }
573 DirectiveError FpySequencer::op_sgt() {
574  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
576  }
577  I64 rhs = this->m_runtime.stack.pop<I64>();
578  I64 lhs = this->m_runtime.stack.pop<I64>();
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_sge() {
584  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
586  }
587  I64 rhs = this->m_runtime.stack.pop<I64>();
588  I64 lhs = this->m_runtime.stack.pop<I64>();
589  this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
590  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
592 }
593 DirectiveError FpySequencer::op_feq() {
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  // eq is true if they are equal and neither is nan
600  this->m_runtime.stack.push(static_cast<U8>((lhs == rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
601  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
603 }
604 DirectiveError FpySequencer::op_fne() {
605  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
607  }
608  F64 rhs = this->m_runtime.stack.pop<F64>();
609  F64 lhs = this->m_runtime.stack.pop<F64>();
610  // ne is true if they are not equal or either is nan
611  this->m_runtime.stack.push(static_cast<U8>((lhs != rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
612  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
614 }
615 DirectiveError FpySequencer::op_flt() {
616  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
618  }
619  F64 rhs = this->m_runtime.stack.pop<F64>();
620  F64 lhs = this->m_runtime.stack.pop<F64>();
621  this->m_runtime.stack.push(static_cast<U8>(std::isless(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
622  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
624 }
625 DirectiveError FpySequencer::op_fle() {
626  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
628  }
629  F64 rhs = this->m_runtime.stack.pop<F64>();
630  F64 lhs = this->m_runtime.stack.pop<F64>();
631  this->m_runtime.stack.push(static_cast<U8>(std::islessequal(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
632  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
634 }
635 DirectiveError FpySequencer::op_fgt() {
636  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
638  }
639  F64 rhs = this->m_runtime.stack.pop<F64>();
640  F64 lhs = this->m_runtime.stack.pop<F64>();
641  this->m_runtime.stack.push(static_cast<U8>(std::isgreater(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
642  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
644 }
645 DirectiveError FpySequencer::op_fge() {
646  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
648  }
649  F64 rhs = this->m_runtime.stack.pop<F64>();
650  F64 lhs = this->m_runtime.stack.pop<F64>();
651  this->m_runtime.stack.push(static_cast<U8>(std::isgreaterequal(lhs, rhs)
652  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
653  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
655 }
656 DirectiveError FpySequencer::op_not() {
657  if (this->m_runtime.stack.size < sizeof(U8)) {
659  }
660  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<U8>() == 0)
661  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
662  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
664 }
665 DirectiveError FpySequencer::op_fpext() {
666  // convert F32 to F64
667  if (this->m_runtime.stack.size < sizeof(F32)) {
669  }
670  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<F32>()));
672 }
673 DirectiveError FpySequencer::op_fptrunc() {
674  // convert F64 to F32
675  if (this->m_runtime.stack.size < sizeof(F64)) {
677  }
678  this->m_runtime.stack.push(static_cast<F32>(this->m_runtime.stack.pop<F64>()));
680 }
681 DirectiveError FpySequencer::op_fptosi() {
682  if (this->m_runtime.stack.size < sizeof(F64)) {
684  }
685  F64 val = this->m_runtime.stack.pop<F64>();
686  // NaN -> 0, out-of-range clamps, in-range truncates toward
687  // zero. The raw static_cast is UB for NaN and out-of-range values.
688  // 2^63 is exactly representable as F64 and is one past I64 max; -2^63
689  // is exactly I64 min and in range.
690  const F64 bound = std::ldexp(1.0, 63);
691  I64 result;
692  if (std::isnan(val)) {
693  result = 0;
694  } else if (val >= bound) {
695  result = std::numeric_limits<I64>::max();
696  } else if (val < -bound) {
698  } else {
699  result = static_cast<I64>(val);
700  }
701  this->m_runtime.stack.push(result);
703 }
704 DirectiveError FpySequencer::op_sitofp() {
705  if (this->m_runtime.stack.size < sizeof(I64)) {
707  }
708  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<I64>()));
710 }
711 DirectiveError FpySequencer::op_fptoui() {
712  if (this->m_runtime.stack.size < sizeof(F64)) {
714  }
715  F64 val = this->m_runtime.stack.pop<F64>();
716  // NaN -> 0, negatives truncate to at most 0 and clamp there,
717  // 2^64 (one past U64 max) and above clamp to U64 max. The raw
718  // static_cast is UB for NaN and out-of-range values.
719  const F64 bound = std::ldexp(1.0, 64);
720  U64 result;
721  if (std::isnan(val) || val < 0.0) {
722  result = 0;
723  } else if (val >= bound) {
724  result = std::numeric_limits<U64>::max();
725  } else {
726  result = static_cast<U64>(val);
727  }
728  this->m_runtime.stack.push(result);
730 }
731 DirectiveError FpySequencer::op_uitofp() {
732  if (this->m_runtime.stack.size < sizeof(U64)) {
734  }
735  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<U64>()));
737 }
738 DirectiveError FpySequencer::op_add() {
739  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
741  }
742  I64 rhs = this->m_runtime.stack.pop<I64>();
743  I64 lhs = this->m_runtime.stack.pop<I64>();
744  // Check for overflow and underflow and return the appropriate error code
745  // Overflow can only occur with both operands positive and occurs when one operand is greater than the maximum value
746  // less the other operand. If either operand is negative or zero, overflow cannot occur.
747  if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() - rhs) < lhs)) {
749  }
750  // Underflow can only occur with both operands negative and occurs when one operand is less than the minimum value
751  // minus the other operand. If either operand is positive or zero, underflow cannot occur.
752  else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() - rhs) > lhs)) {
754  }
755  this->m_runtime.stack.push(static_cast<I64>(lhs + rhs));
757 }
758 DirectiveError FpySequencer::op_sub() {
759  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
761  }
762  I64 rhs = this->m_runtime.stack.pop<I64>();
763  I64 lhs = this->m_runtime.stack.pop<I64>();
764  // Check for overflow and underflow and return the appropriate error code
765  // Overflow can only occur when the left operand is positive and the right operand is negative. It occurs when the
766  // left (positive) operand is greater than the maximum value plus the other (negative) operand. If the right
767  // operand is positive or zero, overflow cannot occur.
768  if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() + rhs) < lhs)) {
770  }
771  // Underflow can only occur when the left operand is negative and the right operand is positive. It occurs when the
772  // left (negative) operand is less than the minimum value plus the other (positive) operand. If the right operand
773  // is negative or zero, underflow cannot occur.
774  else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() + rhs) > lhs)) {
776  }
777  this->m_runtime.stack.push(static_cast<I64>(lhs - rhs));
779 }
780 DirectiveError FpySequencer::op_mul() {
781  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
783  }
784  I64 rhs = this->m_runtime.stack.pop<I64>();
785  I64 lhs = this->m_runtime.stack.pop<I64>();
786  // Check for overflow and underflow and return the appropriate error code
787  // Overflow can only occur with operands of matching signs and occurs when one operand is greater (or less) than the
788  // maximum value divided by the other operand. Either operand being zero precludes overflow.
789  // Check the both positive case.
790  if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() / rhs) < lhs)) {
792  }
793  // Check the both negative case. Compare without negation: negating a value of min is undefined behavior
794  else if ((rhs < 0) && (lhs < 0) && (lhs < (std::numeric_limits<I64>::max() / rhs))) {
796  }
797  // Underflow can occur with operands of differing signs and occurs when one operand is less than the minimum value
798  // divided by the other operand. Either operand being zero precludes underflow.
799  // Check the case where lhs is positive.
800  else if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::min() / lhs) > rhs)) {
802  }
803  // Check the case where rhs is positive.
804  else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() / rhs) > lhs)) {
806  }
807  this->m_runtime.stack.push(static_cast<I64>(lhs * rhs));
809 }
810 DirectiveError FpySequencer::op_udiv() {
811  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
813  }
814  U64 rhs = this->m_runtime.stack.pop<U64>();
815  U64 lhs = this->m_runtime.stack.pop<U64>();
816  // Prevent division by zero
817  if (rhs == 0) {
819  }
820  this->m_runtime.stack.push(static_cast<U64>(lhs / rhs));
822 }
823 DirectiveError FpySequencer::op_sdiv() {
824  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
826  }
827 
828  I64 rhs = this->m_runtime.stack.pop<I64>();
829  I64 lhs = this->m_runtime.stack.pop<I64>();
830  // Prevent division by zero
831  if (rhs == 0) {
833  }
834  // The one signed division that can overflow: |I64 min / -1| = 2^63 is not
835  // representable (and the C++ expression is UB, SIGFPE on x86)
836  if ((lhs == std::numeric_limits<I64>::min()) && (rhs == -1)) {
838  }
839  // C++ / truncates toward zero; adjust to match Python's floored division:
840  // an inexact quotient with differing operand signs floors one below the
841  // truncated result. This mirrors op_smod.
842  I64 quotient = lhs / rhs;
843  if (((lhs % rhs) != 0) && ((lhs < 0) != (rhs < 0))) {
844  quotient -= 1;
845  }
846  this->m_runtime.stack.push(quotient);
848 }
849 DirectiveError FpySequencer::op_umod() {
850  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
852  }
853  U64 rhs = this->m_runtime.stack.pop<U64>();
854  if (rhs == 0) {
856  }
857  U64 lhs = this->m_runtime.stack.pop<U64>();
858  this->m_runtime.stack.push(static_cast<U64>(lhs % rhs));
860 }
861 DirectiveError FpySequencer::op_smod() {
862  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
864  }
865  I64 rhs = this->m_runtime.stack.pop<I64>();
866  if (rhs == 0) {
868  }
869  I64 lhs = this->m_runtime.stack.pop<I64>();
870  // I64 min % -1 is 0, the mathematical remainder (matching wasm i64.rem_s),
871  // but the C++ expression is UB (SIGFPE on x86) so it must be special-cased
872  if ((lhs == std::numeric_limits<I64>::min()) && (rhs == -1)) {
873  this->m_runtime.stack.push(static_cast<I64>(0));
875  }
876  I64 res = static_cast<I64>(lhs % rhs);
877  // in order to match Python's behavior,
878  // if the signs of the remainder and divisor differ, adjust the result.
879  // this happens when the result should be positive but is negative, or vice-versa.
880  // credit Gemini 2.5 pro
881  if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
882  res += rhs;
883  }
884  this->m_runtime.stack.push(res);
886 }
887 DirectiveError FpySequencer::op_fadd() {
888  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
890  }
891  F64 rhs = this->m_runtime.stack.pop<F64>();
892  F64 lhs = this->m_runtime.stack.pop<F64>();
893  this->m_runtime.stack.push(static_cast<F64>(lhs + rhs));
895 }
896 DirectiveError FpySequencer::op_fsub() {
897  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
899  }
900  F64 rhs = this->m_runtime.stack.pop<F64>();
901  F64 lhs = this->m_runtime.stack.pop<F64>();
902  this->m_runtime.stack.push(static_cast<F64>(lhs - rhs));
904 }
905 DirectiveError FpySequencer::op_fmul() {
906  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
908  }
909  F64 rhs = this->m_runtime.stack.pop<F64>();
910  F64 lhs = this->m_runtime.stack.pop<F64>();
911  this->m_runtime.stack.push(static_cast<F64>(lhs * rhs));
913 }
914 DirectiveError FpySequencer::op_fdiv() {
915  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
917  }
918  F64 rhs = this->m_runtime.stack.pop<F64>();
919  F64 lhs = this->m_runtime.stack.pop<F64>();
920  this->m_runtime.stack.push(static_cast<F64>(lhs / rhs));
922 }
923 DirectiveError FpySequencer::op_fpow() {
924  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
926  }
927  F64 rhs = this->m_runtime.stack.pop<F64>();
928  F64 lhs = this->m_runtime.stack.pop<F64>();
929  this->m_runtime.stack.push(static_cast<F64>(pow(lhs, rhs)));
931 }
932 DirectiveError FpySequencer::op_flog() {
933  if (this->m_runtime.stack.size < sizeof(F64)) {
935  }
936  F64 val = this->m_runtime.stack.pop<F64>();
937  if (val <= 0.0) {
939  }
940  this->m_runtime.stack.push(static_cast<F64>(log(val)));
942 }
943 DirectiveError FpySequencer::op_fmod() {
944  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
946  }
947  F64 rhs = this->m_runtime.stack.pop<F64>();
948  F64 lhs = this->m_runtime.stack.pop<F64>();
949  // std::fmod computes the exact truncated remainder (sign of lhs) with no
950  // intermediate rounding. A zero divisor yields NaN, matching Rust and C#.
951  F64 res = std::fmod(lhs, rhs);
952  // Adjust to match Python's floored-modulo semantics: if the signs of the
953  // remainder and divisor differ, add the divisor once. This mirrors op_smod
954  // and is the exact frem + fadd the VM model computes (at most one rounded add).
955  if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
956  res += rhs;
957  } else if (res == 0) {
958  // Python normalizes an exact-multiple result so the zero carries the
959  // divisor's sign (CPython float_rem); fmod leaves the dividend's.
960  res = std::copysign(0.0, rhs);
961  }
962  this->m_runtime.stack.push(res);
964 }
965 DirectiveError FpySequencer::op_siext_8_64() {
966  if (this->m_runtime.stack.size < sizeof(I8)) {
968  }
969  I8 src = this->m_runtime.stack.pop<I8>();
970  this->m_runtime.stack.push(static_cast<I64>(src));
972 }
973 DirectiveError FpySequencer::op_siext_16_64() {
974  if (this->m_runtime.stack.size < sizeof(I16)) {
976  }
977  I16 src = this->m_runtime.stack.pop<I16>();
978  this->m_runtime.stack.push(static_cast<I64>(src));
980 }
981 DirectiveError FpySequencer::op_siext_32_64() {
982  if (this->m_runtime.stack.size < sizeof(I32)) {
984  }
985  I32 src = this->m_runtime.stack.pop<I32>();
986  this->m_runtime.stack.push(static_cast<I64>(src));
988 }
989 DirectiveError FpySequencer::op_ziext_8_64() {
990  if (this->m_runtime.stack.size < sizeof(U8)) {
992  }
993  U8 src = this->m_runtime.stack.pop<U8>();
994  this->m_runtime.stack.push(static_cast<U64>(src));
996 }
997 DirectiveError FpySequencer::op_ziext_16_64() {
998  if (this->m_runtime.stack.size < sizeof(U16)) {
1000  }
1001  U16 src = this->m_runtime.stack.pop<U16>();
1002  this->m_runtime.stack.push(static_cast<U64>(src));
1003  return DirectiveError::NO_ERROR;
1004 }
1005 DirectiveError FpySequencer::op_ziext_32_64() {
1006  if (this->m_runtime.stack.size < sizeof(U32)) {
1008  }
1009  U32 src = this->m_runtime.stack.pop<U32>();
1010  this->m_runtime.stack.push(static_cast<U64>(src));
1011  return DirectiveError::NO_ERROR;
1012 }
1013 DirectiveError FpySequencer::op_itrunc_64_8() {
1014  if (this->m_runtime.stack.size < sizeof(U64)) {
1016  }
1017  U64 src = this->m_runtime.stack.pop<U64>();
1018  this->m_runtime.stack.push(static_cast<U8>(src));
1019  return DirectiveError::NO_ERROR;
1020 }
1021 DirectiveError FpySequencer::op_itrunc_64_16() {
1022  if (this->m_runtime.stack.size < sizeof(U64)) {
1024  }
1025  U64 src = this->m_runtime.stack.pop<U64>();
1026  this->m_runtime.stack.push(static_cast<U16>(src));
1027  return DirectiveError::NO_ERROR;
1028 }
1029 DirectiveError FpySequencer::op_itrunc_64_32() {
1030  if (this->m_runtime.stack.size < sizeof(U64)) {
1032  }
1033  U64 src = this->m_runtime.stack.pop<U64>();
1034  this->m_runtime.stack.push(static_cast<U32>(src));
1035  return DirectiveError::NO_ERROR;
1036 }
1037 DirectiveError FpySequencer::op_ffloor() {
1038  if (this->m_runtime.stack.size < sizeof(F64)) {
1040  }
1041  F64 val = this->m_runtime.stack.pop<F64>();
1042  // std::floor implements IEEE 754 roundToIntegralTowardNegative: +-0, +-inf
1043  // and NaN pass through, and the sign of a zero is preserved.
1044  this->m_runtime.stack.push(std::floor(val));
1045  return DirectiveError::NO_ERROR;
1046 }
1047 DirectiveError FpySequencer::op_iabs() {
1048  if (this->m_runtime.stack.size < sizeof(I64)) {
1050  }
1051  I64 val = this->m_runtime.stack.pop<I64>();
1052  // abs(I64 min) is not representable in I64 (and -val on it is UB)
1053  if (val == std::numeric_limits<I64>::min()) {
1055  }
1056  this->m_runtime.stack.push(val < 0 ? -val : val);
1057  return DirectiveError::NO_ERROR;
1058 }
1059 DirectiveError FpySequencer::op_fabs() {
1060  if (this->m_runtime.stack.size < sizeof(F64)) {
1062  }
1063  F64 val = this->m_runtime.stack.pop<F64>();
1064  // IEEE 754 abs: clears the sign bit and changes nothing else, so NaN
1065  // payloads pass through.
1066  this->m_runtime.stack.push(std::fabs(val));
1067  return DirectiveError::NO_ERROR;
1068 }
1069 Signal FpySequencer::stackOp_directiveHandler(const FpySequencer_StackOpDirective& directive, DirectiveError& error) {
1070  // coding error, should not have gotten to this stack op handler
1071  FW_ASSERT((directive.get__op() >= Fpy::DirectiveId::OR && directive.get__op() <= Fpy::DirectiveId::ITRUNC_64_32) ||
1072  (directive.get__op() >= Fpy::DirectiveId::FFLOOR && directive.get__op() <= Fpy::DirectiveId::FABS),
1073  static_cast<FwAssertArgType>(directive.get__op()));
1074 
1075  switch (directive.get__op()) {
1076  case Fpy::DirectiveId::OR:
1077  error = this->op_or();
1078  break;
1079  case Fpy::DirectiveId::AND:
1080  error = this->op_and();
1081  break;
1082  case Fpy::DirectiveId::IEQ:
1083  error = this->op_ieq();
1084  break;
1085  case Fpy::DirectiveId::INE:
1086  error = this->op_ine();
1087  break;
1088  case Fpy::DirectiveId::ULT:
1089  error = this->op_ult();
1090  break;
1091  case Fpy::DirectiveId::ULE:
1092  error = this->op_ule();
1093  break;
1094  case Fpy::DirectiveId::UGT:
1095  error = this->op_ugt();
1096  break;
1097  case Fpy::DirectiveId::UGE:
1098  error = this->op_uge();
1099  break;
1100  case Fpy::DirectiveId::SLT:
1101  error = this->op_slt();
1102  break;
1103  case Fpy::DirectiveId::SLE:
1104  error = this->op_sle();
1105  break;
1106  case Fpy::DirectiveId::SGT:
1107  error = this->op_sgt();
1108  break;
1109  case Fpy::DirectiveId::SGE:
1110  error = this->op_sge();
1111  break;
1112  case Fpy::DirectiveId::FEQ:
1113  error = this->op_feq();
1114  break;
1115  case Fpy::DirectiveId::FNE:
1116  error = this->op_fne();
1117  break;
1118  case Fpy::DirectiveId::FLT:
1119  error = this->op_flt();
1120  break;
1121  case Fpy::DirectiveId::FLE:
1122  error = this->op_fle();
1123  break;
1124  case Fpy::DirectiveId::FGT:
1125  error = this->op_fgt();
1126  break;
1127  case Fpy::DirectiveId::FGE:
1128  error = this->op_fge();
1129  break;
1130  case Fpy::DirectiveId::NOT:
1131  error = this->op_not();
1132  break;
1134  error = this->op_fpext();
1135  break;
1137  error = this->op_fptrunc();
1138  break;
1140  error = this->op_fptosi();
1141  break;
1143  error = this->op_fptoui();
1144  break;
1146  error = this->op_sitofp();
1147  break;
1149  error = this->op_uitofp();
1150  break;
1151  case Fpy::DirectiveId::ADD:
1152  error = this->op_add();
1153  break;
1154  case Fpy::DirectiveId::SUB:
1155  error = this->op_sub();
1156  break;
1157  case Fpy::DirectiveId::MUL:
1158  error = this->op_mul();
1159  break;
1161  error = this->op_udiv();
1162  break;
1164  error = this->op_sdiv();
1165  break;
1167  error = this->op_umod();
1168  break;
1170  error = this->op_smod();
1171  break;
1173  error = this->op_fadd();
1174  break;
1176  error = this->op_fsub();
1177  break;
1179  error = this->op_fmul();
1180  break;
1182  error = this->op_fdiv();
1183  break;
1185  error = this->op_fpow();
1186  break;
1188  error = this->op_flog();
1189  break;
1191  error = this->op_fmod();
1192  break;
1194  error = this->op_siext_8_64();
1195  break;
1197  error = this->op_siext_16_64();
1198  break;
1200  error = this->op_siext_32_64();
1201  break;
1203  error = this->op_ziext_8_64();
1204  break;
1206  error = this->op_ziext_16_64();
1207  break;
1209  error = this->op_ziext_32_64();
1210  break;
1212  error = this->op_itrunc_64_8();
1213  break;
1215  error = this->op_itrunc_64_16();
1216  break;
1218  error = this->op_itrunc_64_32();
1219  break;
1221  error = this->op_ffloor();
1222  break;
1224  error = this->op_iabs();
1225  break;
1227  error = this->op_fabs();
1228  break;
1229  default:
1230  FW_ASSERT(false, directive.get__op());
1231  break;
1232  }
1233  if (error != DirectiveError::NO_ERROR) {
1235  }
1237 }
1238 
1239 Signal FpySequencer::exit_directiveHandler(const FpySequencer_ExitDirective& directive, DirectiveError& error) {
1240  if (this->m_runtime.stack.size < sizeof(I32)) {
1243  }
1244  I32 errorCode = this->m_runtime.stack.pop<I32>();
1245  // exit(0), no error
1246  if (errorCode == 0) {
1247  // just goto the end of the sequence
1248  this->m_runtime.nextStatementIndex = this->m_sequenceObj.get_header().get_statementCount();
1250  }
1251  // otherwise, kill the sequence here
1252  // raise the user defined error code as an event
1253  this->log_WARNING_HI_SequenceExitedWithError(this->m_sequenceFilePath, errorCode);
1256 }
1257 
1258 Signal FpySequencer::allocate_directiveHandler(const FpySequencer_AllocateDirective& directive, DirectiveError& error) {
1259  if (directive.get_size() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1262  }
1263  this->m_runtime.stack.pushZeroes(directive.get_size());
1265 }
1266 
1268 Signal FpySequencer::storeHelper(Fpy::StackSizeType destOffset, Fpy::StackSizeType size, DirectiveError& error) {
1269  if (this->m_runtime.stack.size < size) {
1272  }
1273  // After popping the value, would the write go out of bounds?
1274  Fpy::StackSizeType newStackSize = this->m_runtime.stack.size - size;
1275  // Overflow-safe check: destOffset + size > newStackSize
1276  // Rewritten as: check destOffset <= newStackSize first, then size > newStackSize - destOffset
1277  if (destOffset > newStackSize || size > newStackSize - destOffset) {
1280  }
1281  // Copy value to the destination location
1282  this->m_runtime.stack.copy(destOffset, this->m_runtime.stack.size - size, size);
1283  this->m_runtime.stack.size = newStackSize;
1285 }
1286 
1288 Signal FpySequencer::loadHelper(Fpy::StackSizeType srcOffset, Fpy::StackSizeType size, DirectiveError& error) {
1289  if (size > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1292  }
1293  // Overflow-safe check: srcOffset + size > stack.size
1294  // Rewritten as: check srcOffset <= stack.size first, then size > stack.size - srcOffset
1295  if (srcOffset > this->m_runtime.stack.size || size > this->m_runtime.stack.size - srcOffset) {
1298  }
1299  // Copy from source location to top of stack
1300  this->m_runtime.stack.copy(this->m_runtime.stack.size, srcOffset, size);
1301  this->m_runtime.stack.size += size;
1303 }
1304 
1305 Signal FpySequencer::storeRelConstOffset_directiveHandler(const FpySequencer_StoreRelConstOffsetDirective& directive,
1306  DirectiveError& error) {
1307  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1308  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1311  }
1312  return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1313 }
1314 
1315 Signal FpySequencer::loadRel_directiveHandler(const FpySequencer_LoadRelDirective& directive, DirectiveError& error) {
1316  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1317  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1320  }
1321  return this->loadHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1322 }
1323 
1324 Signal FpySequencer::pushVal_directiveHandler(const FpySequencer_PushValDirective& directive, DirectiveError& error) {
1325  if (directive.get__valSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1328  }
1329  // copy from the bytearray in the directive to the stack, add to stack size.
1330  this->m_runtime.stack.push(const_cast<U8*>(directive.get_val()),
1331  static_cast<Fpy::StackSizeType>(directive.get__valSize()));
1333 }
1334 
1335 Signal FpySequencer::discard_directiveHandler(const FpySequencer_DiscardDirective& directive, DirectiveError& error) {
1336  if (this->m_runtime.stack.size < directive.get_size()) {
1339  }
1340  // drop the specified amount of bytes off the stack. simple as.
1341  this->m_runtime.stack.size -= directive.get_size();
1343 }
1344 
1345 Signal FpySequencer::memCmp_directiveHandler(const FpySequencer_MemCmpDirective& directive, DirectiveError& error) {
1346  // Overflow-safe check: we need size * 2 bytes on the stack
1347  // First check that size * 2 doesn't overflow: size > MAX/2 would overflow
1348  // MAX_STACK_SIZE is the upper bound for stack.size, so if size > MAX_STACK_SIZE/2, we definitely don't have enough
1349  if (directive.get_size() > Fpy::MAX_STACK_SIZE / 2) {
1352  }
1353  // Now safe to compute size * 2
1354  if (this->m_runtime.stack.size < directive.get_size() * 2) {
1357  }
1358 
1359  // find the starting offsets of the two byte arrays
1360  U64 lhsOffset = this->m_runtime.stack.size - directive.get_size() * 2;
1361  U64 rhsOffset = this->m_runtime.stack.size - directive.get_size();
1362 
1363  // "officially" remove them from the stack
1364  // you have to do this before pushing to the stack, otherwise the result would get placed
1365  // after the byte arrays
1366  this->m_runtime.stack.size -= directive.get_size() * 2;
1367 
1368  // memcmp the two byte arrays, push FW_SERIALIZE_TRUE_VALUE if they were equal, FW_SERIALIZE_FALSE_VALUE otherwise
1369  if (memcmp(this->m_runtime.stack.bytes + lhsOffset, this->m_runtime.stack.bytes + rhsOffset,
1370  directive.get_size()) == 0) {
1371  this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_TRUE_VALUE));
1372  } else {
1373  this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_FALSE_VALUE));
1374  }
1376 }
1377 
1378 Signal FpySequencer::stackCmd_directiveHandler(const FpySequencer_StackCmdDirective& directive, DirectiveError& error) {
1379  // Overflow-safe check: need argsSize + sizeof(FwOpcodeType) bytes
1380  // Check stack.size >= sizeof(FwOpcodeType) first, then stack.size - sizeof(FwOpcodeType) >= argsSize
1381  if (this->m_runtime.stack.size < sizeof(FwOpcodeType) ||
1382  this->m_runtime.stack.size - sizeof(FwOpcodeType) < directive.get_argsSize()) {
1385  }
1386 
1387  // pop the opcode of the cmd off the stack
1388  // note this means that, unlike the actual byte array that the dispatcher gets,
1389  // these cmds have opcode after the argument buffer
1390  FwOpcodeType opcode = this->m_runtime.stack.pop<FwOpcodeType>();
1391  U64 argBufOffset = this->m_runtime.stack.size - directive.get_argsSize();
1392 
1393  // update the opcode of the cmd we will await
1394  this->m_runtime.currentCmdOpcode = opcode;
1395 
1396  // also pop the args off the stack
1397  this->m_runtime.stack.size -= directive.get_argsSize();
1398 
1399  // the cmd response code will be pushed to the stack when it comes back, so make sure
1400  // there is room for it now, before the cmd is dispatched. popping the opcode above
1401  // frees some room, but FwOpcodeType is configurable so it may not be enough
1402  if (sizeof(Fw::CmdResponse::SerialType) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1405  }
1406 
1407  if (this->sendCmd(opcode, this->m_runtime.stack.bytes + argBufOffset, directive.get_argsSize()) ==
1410  } else {
1411  // now tell the SM to wait some more until we get the cmd response back
1412  // if we've already got the response back this should be harmless
1414  }
1415 
1417 }
1418 
1419 Signal FpySequencer::pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& error) {
1420  if (Fw::Time::SERIALIZED_SIZE > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1423  }
1424 
1425  Fw::Time currentTime = this->getTime();
1426 
1427  U8 currentTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
1428  Fw::ExternalSerializeBuffer timeEsb(currentTimeBuf, Fw::Time::SERIALIZED_SIZE);
1429  Fw::SerializeStatus stat = timeEsb.serializeFrom(currentTime);
1430 
1431  // coding error if this failed, we should have enough space
1432  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1433 
1434  // push time to end of stack
1435  this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
1437 }
1438 
1439 Signal FpySequencer::setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& error) {
1440  if (this->m_runtime.stack.size < sizeof(U32)) {
1443  }
1444 
1445  U32 seed = this->m_runtime.stack.pop<U32>();
1446  this->m_runtime.rng.seed(seed);
1447  this->m_runtime.rngSeeded = true;
1449 }
1450 
1451 Signal FpySequencer::pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& error) {
1452  if (sizeof(U32) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1455  }
1456 
1457  if (!this->m_runtime.rngSeeded) {
1458  Fw::Time currentTime = this->getTime();
1459  std::seed_seq seedSeq{static_cast<U32>(currentTime.getTimeBase()), static_cast<U32>(currentTime.getContext()),
1460  currentTime.getSeconds(), currentTime.getUSeconds()};
1461  this->m_runtime.rng.seed(seedSeq);
1462  this->m_runtime.rngSeeded = true;
1463  }
1464 
1465  U32 randVal = static_cast<U32>(this->m_runtime.rng());
1466  this->m_runtime.stack.push(randVal);
1468 }
1469 
1470 Signal FpySequencer::getField_directiveHandler(const FpySequencer_GetFieldDirective& directive, DirectiveError& error) {
1471  // Need sizeof(StackSizeType) for the offset AND parentSize for the parent data
1472  // Check we have enough for the offset first
1473  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1476  }
1477  // After popping the offset, we need at least parentSize bytes remaining
1478  if (this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < directive.get_parentSize()) {
1481  }
1482 
1483  Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1484 
1485  // Overflow-safe check: offset + memberSize > parentSize
1486  // Rewritten as: check offset <= parentSize first, then memberSize > parentSize - offset
1487  if (offset > directive.get_parentSize() || directive.get_memberSize() > directive.get_parentSize() - offset) {
1488  // i think it's somewhat ambiguous whether this is a stack access out of bounds
1489  // but there isn't really an error code that better reflects this, and i guess
1490  // it's technically true
1493  }
1494 
1495  // the resulting bytes should move to the start of the parent array
1496 
1497  // Calculate the offset of the parent start in the stack
1498  Fpy::StackSizeType parentStartOffset = this->m_runtime.stack.size - directive.get_parentSize();
1499  // Overflow-safe: parentStartOffset + offset cannot overflow since offset <= parentSize
1500  // and parentStartOffset + parentSize == stack.size (which is bounded)
1501  this->m_runtime.stack.move(parentStartOffset, parentStartOffset + offset, directive.get_memberSize());
1502  // adjust stack size by the diff between the member and the parent
1503  this->m_runtime.stack.size -= (directive.get_parentSize() - directive.get_memberSize());
1505 }
1506 
1507 Signal FpySequencer::peek_directiveHandler(const FpySequencer_PeekDirective& directive, DirectiveError& error) {
1508  // must have at least two StackSizeType on stack
1509  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) * 2) {
1512  }
1513 
1514  Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1515  Fpy::StackSizeType byteCount = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1516 
1517  // Check offset doesn't exceed stack size (after both pops)
1518  if (offset > this->m_runtime.stack.size) {
1519  // would access past the bottom of the stack
1520  // note we allow the equals case because the byteCount might be 0
1523  }
1524  if (byteCount > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1525  // we would overflow the stack if we pushed this many bytes to it
1528  }
1529  // Overflow-safe check: byteCount + offset > stack.size
1530  // Rewritten as: check offset <= stack.size (done above), then byteCount > stack.size - offset
1531  if (byteCount > this->m_runtime.stack.size - offset) {
1532  // would access past the bottom of the stack
1535  }
1536  // start copying from the lowest byte of the src array
1537  U8* src = this->m_runtime.stack.top() - offset - byteCount;
1538  this->m_runtime.stack.push(src, byteCount);
1540 }
1541 
1542 Signal FpySequencer::storeRel_directiveHandler(const FpySequencer_StoreRelDirective& directive, DirectiveError& error) {
1543  // Need enough bytes for the value and the offset (SignedStackSizeType = 4 bytes)
1544  // Overflow-safe: check stack.size >= sizeof(SignedStackSizeType) first, then stack.size -
1545  // sizeof(SignedStackSizeType) >= size
1546  if (this->m_runtime.stack.size < sizeof(Fpy::SignedStackSizeType) ||
1547  this->m_runtime.stack.size - sizeof(Fpy::SignedStackSizeType) < directive.get_size()) {
1550  }
1551 
1552  // Pop the signed offset from the stack
1553  Fpy::SignedStackSizeType lvarOffset = this->m_runtime.stack.pop<Fpy::SignedStackSizeType>();
1554 
1555  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + lvarOffset;
1556  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1559  }
1560  return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1561 }
1562 
1563 Signal FpySequencer::call_directiveHandler(const FpySequencer_CallDirective& directive, DirectiveError& error) {
1564  // Need at least 4 bytes for the target address
1565  if (this->m_runtime.stack.size < sizeof(U32)) {
1568  }
1569 
1570  // Pop the target directive index from the stack
1571  U32 target = this->m_runtime.stack.pop<U32>();
1572 
1573  // Check if we have space to push return address and saved frame pointer (8 bytes total)
1574  if (this->m_runtime.stack.size + sizeof(Fpy::StackSizeType) + sizeof(U32) > Fpy::MAX_STACK_SIZE) {
1577  }
1578 
1579  // Check target is within bounds (will also be checked at execution time)
1580  if (target > m_sequenceObj.get_header().get_statementCount()) {
1583  }
1584 
1585  // Save the return address (next instruction after CALL)
1586  U32 returnAddr = this->m_runtime.nextStatementIndex;
1587 
1588  // Set the next instruction to the target
1589  this->m_runtime.nextStatementIndex = target;
1590 
1591  // Push the return address to the stack
1592  this->m_runtime.stack.push<U32>(returnAddr);
1593 
1594  // Push the current frame pointer to the stack
1595  this->m_runtime.stack.push<Fpy::StackSizeType>(this->m_runtime.stack.currentFrameStart);
1596 
1597  // Set the new frame pointer to the current top of stack
1598  this->m_runtime.stack.currentFrameStart = this->m_runtime.stack.size;
1599 
1601 }
1602 
1603 Signal FpySequencer::return_directiveHandler(const FpySequencer_ReturnDirective& directive, DirectiveError& error) {
1604  Fpy::StackSizeType returnValSize = directive.get_returnValSize();
1605  Fpy::StackSizeType callArgsSize = directive.get_callArgsSize();
1606 
1607  // Check we have enough bytes for the return value
1608  if (this->m_runtime.stack.size < returnValSize) {
1611  }
1612 
1613  // Remember where the return value lives; it is moved down the stack below rather than copied
1614  // through a local buffer, which at Fpy::MAX_STACK_SIZE would not fit a typical task stack
1615  const Fpy::StackSizeType returnValOffset = this->m_runtime.stack.size - returnValSize;
1616 
1617  // Truncate the stack to stack_frame_start (discard all local variables)
1618  if (this->m_runtime.stack.currentFrameStart > this->m_runtime.stack.size) {
1621  }
1622  this->m_runtime.stack.size = this->m_runtime.stack.currentFrameStart;
1623 
1624  // Check we have enough bytes for saved frame pointer and return address
1625  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) + sizeof(U32)) {
1628  }
1629 
1630  // Pop the saved frame pointer
1631  Fpy::StackSizeType savedFramePtr = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1632 
1633  // Pop the return address
1634  U32 returnAddr = this->m_runtime.stack.pop<U32>();
1635 
1636  // Restore the frame pointer
1637  if (savedFramePtr > this->m_runtime.stack.size) {
1640  }
1641  this->m_runtime.stack.currentFrameStart = savedFramePtr;
1642 
1643  // Validate the return address is within bounds
1644  if (returnAddr > m_sequenceObj.get_header().get_statementCount()) {
1647  }
1648 
1649  // Set the next instruction to the return address
1650  this->m_runtime.nextStatementIndex = returnAddr;
1651 
1652  // Check that we have enough bytes for the call arguments
1653  if (this->m_runtime.stack.size < callArgsSize) {
1656  }
1657  // Discard the function arguments
1658  this->m_runtime.stack.size -= callArgsSize;
1659 
1660  // Push the return value
1661  if (returnValSize > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1664  }
1665  if (returnValSize > 0) {
1666  // Not Stack::move: the source region sits above the truncated stack size, which
1667  // Stack::move rejects. Both regions were bounds-checked above against MAX_STACK_SIZE.
1668  (void)memmove(this->m_runtime.stack.top(), &this->m_runtime.stack.bytes[returnValOffset], returnValSize);
1669  this->m_runtime.stack.size += returnValSize;
1670  }
1671 
1673 }
1674 
1675 Signal FpySequencer::loadAbs_directiveHandler(const FpySequencer_LoadAbsDirective& directive, DirectiveError& error) {
1676  return this->loadHelper(directive.get_globalOffset(), directive.get_size(), error);
1677 }
1678 
1679 Signal FpySequencer::storeAbs_directiveHandler(const FpySequencer_StoreAbsDirective& directive, DirectiveError& error) {
1680  Fpy::StackSizeType size = directive.get_size();
1681 
1682  // Need enough bytes for the value and the offset
1683  // Overflow-safe: check stack.size >= sizeof(StackSizeType) first, then stack.size - sizeof >= size
1684  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) ||
1685  this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < size) {
1688  }
1689 
1690  // Pop the global offset from the stack
1691  Fpy::StackSizeType globalOffset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1692 
1693  return this->storeHelper(globalOffset, size, error);
1694 }
1695 
1696 Signal FpySequencer::storeAbsConstOffset_directiveHandler(const FpySequencer_StoreAbsConstOffsetDirective& directive,
1697  DirectiveError& error) {
1698  return this->storeHelper(directive.get_globalOffset(), directive.get_size(), error);
1699 }
1700 
1701 Signal FpySequencer::popEvent_directiveHandler(const FpySequencer_PopEventDirective& directive, DirectiveError& error) {
1702  // Pop messageSize from the stack
1703  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1706  }
1707  Fpy::StackSizeType messageSize = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1708 
1709  const Fpy::StackSizeType severitySize = static_cast<Fpy::StackSizeType>(sizeof(Fw::LogSeverity::SerialType));
1710 
1711  // Need message_size bytes + sizeof(LogSeverity serial type) for severity
1712  if (this->m_runtime.stack.size < severitySize || this->m_runtime.stack.size - severitySize < messageSize) {
1715  }
1716 
1717  // Pop message bytes first
1718  U8 messageBuf[FW_LOG_STRING_MAX_SIZE] = {};
1719  // don't read in more than (log string size) - 1 bytes
1720  Fpy::StackSizeType clampedSize = std::min(messageSize, static_cast<Fpy::StackSizeType>(FW_LOG_STRING_MAX_SIZE - 1));
1721  // If message is larger than buffer, discard the excess bytes first (from top of stack, which is the end of the
1722  // message)
1723  if (messageSize > clampedSize) {
1724  Fpy::StackSizeType excess = messageSize - clampedSize;
1725  FW_ASSERT(this->m_runtime.stack.size >= excess, static_cast<FwAssertArgType>(this->m_runtime.stack.size),
1726  static_cast<FwAssertArgType>(excess));
1727  this->m_runtime.stack.size -= excess;
1728  }
1729  this->m_runtime.stack.pop(messageBuf, clampedSize);
1730  messageBuf[clampedSize] = '\0';
1731 
1732  // Pop severity
1733  Fw::LogSeverity::SerialType severity = this->m_runtime.stack.pop<Fw::LogSeverity::SerialType>();
1734 
1735  // Construct the message string
1736  Fw::String messageStr(reinterpret_cast<const char*>(messageBuf));
1737 
1738  // Emit the appropriate event based on severity
1739  switch (severity) {
1741  this->log_FATAL_LogFatal(this->m_sequenceFilePath, messageStr);
1742  break;
1744  this->log_WARNING_HI_LogWarningHi(this->m_sequenceFilePath, messageStr);
1745  break;
1747  this->log_WARNING_LO_LogWarningLo(this->m_sequenceFilePath, messageStr);
1748  break;
1750  this->log_COMMAND_LogCommand(this->m_sequenceFilePath, messageStr);
1751  break;
1753  this->log_ACTIVITY_HI_LogActivityHi(this->m_sequenceFilePath, messageStr);
1754  break;
1756  this->log_ACTIVITY_LO_LogActivityLo(this->m_sequenceFilePath, messageStr);
1757  break;
1759  this->log_DIAGNOSTIC_LogDiagnostic(this->m_sequenceFilePath, messageStr);
1760  break;
1761  default:
1764  }
1765 
1767 }
1768 
1769 Signal FpySequencer::popSerializable_directiveHandler(const FpySequencer_PopSerializableDirective& directive,
1770  DirectiveError& error) {
1771  // No size assertion here: an oversized size is untrusted sequence content and is rejected by
1772  // the stack check below, since the stack can never hold more than Fpy::MAX_STACK_SIZE bytes
1773 
1774  // Validate port index is in range (using enum constant value)
1775  constexpr FwIndexType MAX_PORTS = static_cast<FwIndexType>(Svc::Fpy::SerialPortIndex::MAX_SERIAL_PORTS);
1776  const FwIndexType portIndex = directive.get_portIndex();
1777 
1778  // Check for negative port index or out of bounds
1779  if (portIndex < 0 || portIndex >= MAX_PORTS) {
1782  }
1783 
1784  // Check port is connected
1785  if (!this->isConnected_serialOut_OutputPort(portIndex)) {
1788  }
1789 
1790  // Validate data size on stack
1791  if (this->m_runtime.stack.size < directive.get_size()) {
1794  }
1795 
1796  // Create external buffer referencing stack data (no copy)
1797  U8* dataPtr = this->m_runtime.stack.top() - directive.get_size();
1798  Fw::ExternalSerializeBuffer buf(dataPtr, directive.get_size());
1799 
1800  // Set buffer length and verify success
1801  Fw::SerializeStatus stat = buf.setBuffLen(directive.get_size());
1802  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1803 
1804  // Call output port; a typed downstream port reports deserialize failures here, which is
1805  // untrusted sequence content (e.g. undersized payload), not an invariant to assert on
1806  Fw::SerializeStatus portStatus = this->serialOut_out(portIndex, buf);
1807  if (portStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
1810  }
1811 
1812  // Pop data from stack
1813  this->m_runtime.stack.size -= directive.get_size();
1814 
1816 }
1817 
1818 } // 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
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.
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.
bool isConnected_getParam_OutputPort(FwIndexType portNum) const
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 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:51
stores a value to a local variable at a compile-time-known offset relative to the current stack frame...
Fw::SerializeStatus serialOut_out(FwIndexType portNum, Fw::LinearBufferBase &buffer)
Invoke output port serialOut.
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.
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:136
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.
SerializeStatus serializeFrom(U8 val, Endianness mode=Endianness::BIG) override
Serialize an 8-bit unsigned integer value.
void directive_pushPrm_internalInterfaceHandler(const Svc::FpySequencer_PushPrmDirective &directive) override
Internal interface handler for directive_pushPrm.
REQUIRED: Maximum number of serial ports. This sentinel value MUST be named.
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
void log_WARNING_HI_SequenceExitedWithError(const Fw::StringBase &filePath, I32 errorCode) const
Log event SequenceExitedWithError.
float F32
32-bit floating point
Definition: BasicTypes.h:84
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
U8 * getBuffAddr()
Get buffer address for data filling (non-const version)
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:128
Svc::Fpy::DirectiveId::T get__op() const
Get member _op.
U8 SerialType
The serial representation type.
void directive_waitAbs_internalInterfaceHandler(const FpySequencer_WaitAbsDirective &directive) override
Internal interface handler for directive_waitAbs.
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.
void directive_popSerializable_internalInterfaceHandler(const Svc::FpySequencer_PopSerializableDirective &directive) override
Internal interface handler for directive_popSerializable.
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:54
bool isConnected_serialOut_OutputPort(FwIndexType portNum) const
SerializeStatus setBuffLen(Serializable::SizeType length) override
Set buffer length manually.
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
U8 SerialType
The serial representation type.
static Time add(const Time &a, const Time &b)
Definition: Time.cpp:174
void sequencer_sendSignal_stmtResponse_keepWaiting()
Send signal stmtResponse_keepWaiting to state machine sequencer.
FwTimeContextStoreType getContext() const
Definition: Time.cpp:140
U32 getUSeconds() const
Definition: Time.cpp:132
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)
PlatformIndexType FwIndexType
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:86
pops serialized data from stack and sends to serial output port
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.
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.