GCC Code Coverage Report


Directory: ./
File: Svc/FpySequencer/FpySequencerDirectives.cpp
Date: 2026-11-03 21:15:54
Exec Total Coverage
Lines: 1071 1230 87.1%
Functions: 119 119 100.0%
Branches: 861 1052 81.8%

Line Branch Exec Source
1 #include <algorithm>
2 #include <cmath>
3 #include <cstring>
4 #include <type_traits>
5 #include "Fw/Com/ComPacket.hpp"
6 #include "Svc/FpySequencer/FpySequencer.hpp"
7 #include "config/SerialPortIndexEnumAc.hpp"
8
9 namespace Svc {
10
11 2192 void FpySequencer::sendSignal(Signal signal) {
12
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2179 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
2192 switch (signal) {
13 5 case Signal::stmtResponse_beginSleep: {
14 5 this->sequencer_sendSignal_stmtResponse_beginSleep();
15 5 break;
16 }
17 2179 case Signal::stmtResponse_success: {
18 2179 this->sequencer_sendSignal_stmtResponse_success();
19 2179 break;
20 }
21 3 case Signal::stmtResponse_failure: {
22 3 this->sequencer_sendSignal_stmtResponse_failure();
23 3 break;
24 }
25 5 case Signal::stmtResponse_keepWaiting: {
26 5 this->sequencer_sendSignal_stmtResponse_keepWaiting();
27 5 break;
28 }
29 ✗ default: {
30 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(signal));
31 }
32 }
33 2192 }
34
35 // utility method for updating telemetry based on a directive error code
36 2192 void FpySequencer::handleDirectiveErrorCode(Fpy::DirectiveId id, DirectiveError err) {
37 2192 this->m_tlm.lastDirectiveError = err;
38
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2189 times.
2192 if (err != DirectiveError::NO_ERROR) {
39 3 this->m_tlm.directiveErrorIndex = this->currentStatementIdx();
40 3 this->m_tlm.directiveErrorId = id;
41 }
42 2192 }
43
44 11 Fw::Success FpySequencer::sendCmd(FwOpcodeType opcode, const U8* argBuf, FwSizeType argBufSize) {
45
1/1
✓ Branch 2 taken 11 times.
11 Fw::ComBuffer cmdBuf;
46 Fw::SerializeStatus stat =
47
1/1
✓ Branch 2 taken 11 times.
11 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
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stat != Fw::SerializeStatus::FW_SERIALIZE_OK) {
51 ✗ return Fw::Success::FAILURE;
52 }
53
1/1
✓ Branch 2 taken 11 times.
11 stat = cmdBuf.serializeFrom(opcode);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stat != Fw::SerializeStatus::FW_SERIALIZE_OK) {
55 ✗ return Fw::Success::FAILURE;
56 }
57
1/1
✓ Branch 2 taken 11 times.
11 stat = cmdBuf.serializeFrom(argBuf, argBufSize, Fw::Serialization::OMIT_LENGTH);
58
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stat != Fw::SerializeStatus::FW_SERIALIZE_OK) {
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 11 static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_statementsDispatched & 0xFFFF));
71
72
1/1
✓ Branch 5 taken 11 times.
11 this->cmdOut_out(0, cmdBuf, cmdUid);
73
74
1/1
✓ Branch 1 taken 11 times.
11 return Fw::Success::SUCCESS;
75 11 }
76
77 // returns true if a directive can be executed. a directive is only ever dispatched in
78 // RUNNING.DISPATCH_STATEMENT, after which the sequencer waits in RUNNING.AWAITING_STATEMENT_RESPONSE.
79 // CANCEL and RUN are higher priority than directives, so a sequence can be cancelled while its directive is
80 // still queued, and a new sequence can be started and dispatch its first statement before that directive is
81 // serviced. neither of those directives may execute
82 2257 bool FpySequencer::checkDirectiveExpected(Fpy::DirectiveId directive, U64 sequenceIndex) {
83
3/3
✓ Branch 3 taken 2257 times.
✓ Branch 12 taken 32 times.
✓ Branch 13 taken 2225 times.
2257 if (this->sequencer_getState() != State::RUNNING_AWAITING_STATEMENT_RESPONSE) {
84
2/2
✓ Branch 7 taken 32 times.
✓ Branch 14 taken 32 times.
32 this->log_WARNING_LO_DirectiveWhileNotAwaiting(directive, static_cast<I32>(this->sequencer_getState()));
85 32 return false;
86 }
87
2/2
✓ Branch 4 taken 33 times.
✓ Branch 5 taken 2192 times.
2225 if (sequenceIndex != this->m_sequencesStarted) {
88 33 this->log_WARNING_LO_DirectiveFromOldSequence(directive, sequenceIndex, this->m_sequencesStarted);
89 33 return false;
90 }
91 2192 return true;
92 }
93
94 //! Internal interface handler for directive_waitRel
95 6 void FpySequencer::directive_waitRel_internalInterfaceHandler(const FpySequencer_WaitRelDirective& directive,
96 U64 sequenceIndex) {
97
4/4
✓ Branch 5 taken 6 times.
✓ Branch 8 taken 6 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 4 times.
6 if (!this->checkDirectiveExpected(Fpy::DirectiveId::WAIT_REL, sequenceIndex)) {
98 2 return;
99 }
100
1/1
✓ Branch 2 taken 4 times.
4 DirectiveError error = DirectiveError::NO_ERROR;
101
2/2
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->sendSignal(this->waitRel_directiveHandler(directive, error));
102
3/3
✓ Branch 5 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 12 taken 4 times.
4 handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_REL, error);
103 4 }
104
105 //! Internal interface handler for directive_waitAbs
106 3 void FpySequencer::directive_waitAbs_internalInterfaceHandler(const FpySequencer_WaitAbsDirective& directive,
107 U64 sequenceIndex) {
108
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::WAIT_ABS, sequenceIndex)) {
109 2 return;
110 }
111
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
112
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->waitAbs_directiveHandler(directive, error));
113
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_ABS, error);
114 1 }
115
116 //! Internal interface handler for directive_goto
117 4 void FpySequencer::directive_goto_internalInterfaceHandler(const Svc::FpySequencer_GotoDirective& directive,
118 U64 sequenceIndex) {
119
4/4
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 4 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 2 times.
4 if (!this->checkDirectiveExpected(Fpy::DirectiveId::GOTO, sequenceIndex)) {
120 2 return;
121 }
122
1/1
✓ Branch 2 taken 2 times.
2 DirectiveError error = DirectiveError::NO_ERROR;
123
2/2
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->sendSignal(this->goto_directiveHandler(directive, error));
124
3/3
✓ Branch 5 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 2 times.
2 handleDirectiveErrorCode(Fpy::DirectiveId::GOTO, error);
125 2 }
126
127 //! Internal interface handler for directive_if
128 9 void FpySequencer::directive_if_internalInterfaceHandler(const Svc::FpySequencer_IfDirective& directive,
129 U64 sequenceIndex) {
130
4/4
✓ Branch 5 taken 9 times.
✓ Branch 8 taken 9 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 7 times.
9 if (!this->checkDirectiveExpected(Fpy::DirectiveId::IF, sequenceIndex)) {
131 2 return;
132 }
133
1/1
✓ Branch 2 taken 7 times.
7 DirectiveError error = DirectiveError::NO_ERROR;
134
2/2
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
7 this->sendSignal(this->if_directiveHandler(directive, error));
135
3/3
✓ Branch 5 taken 7 times.
✓ Branch 9 taken 7 times.
✓ Branch 12 taken 7 times.
7 handleDirectiveErrorCode(Fpy::DirectiveId::IF, error);
136 7 }
137
138 //! Internal interface handler for directive_noOp
139 2059 void FpySequencer::directive_noOp_internalInterfaceHandler(const Svc::FpySequencer_NoOpDirective& directive,
140 U64 sequenceIndex) {
141
4/4
✓ Branch 5 taken 2059 times.
✓ Branch 8 taken 2059 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 2057 times.
2059 if (!this->checkDirectiveExpected(Fpy::DirectiveId::NO_OP, sequenceIndex)) {
142 2 return;
143 }
144
1/1
✓ Branch 2 taken 2057 times.
2057 DirectiveError error = DirectiveError::NO_ERROR;
145
1/1
✓ Branch 8 taken 2057 times.
2057 this->sendSignal(this->noOp_directiveHandler(directive, error));
146
3/3
✓ Branch 5 taken 2057 times.
✓ Branch 9 taken 2057 times.
✓ Branch 12 taken 2057 times.
2057 handleDirectiveErrorCode(Fpy::DirectiveId::NO_OP, error);
147 2057 }
148
149 //! Internal interface handler for directive_pushTlmVal
150 8 void FpySequencer::directive_pushTlmVal_internalInterfaceHandler(const Svc::FpySequencer_PushTlmValDirective& directive,
151 U64 sequenceIndex) {
152
4/4
✓ Branch 5 taken 8 times.
✓ Branch 8 taken 8 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 6 times.
8 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PUSH_TLM_VAL, sequenceIndex)) {
153 2 return;
154 }
155
1/1
✓ Branch 2 taken 6 times.
6 DirectiveError error = DirectiveError::NO_ERROR;
156
2/2
✓ Branch 7 taken 6 times.
✓ Branch 10 taken 6 times.
6 this->sendSignal(this->pushTlmVal_directiveHandler(directive, error));
157
3/3
✓ Branch 5 taken 6 times.
✓ Branch 9 taken 6 times.
✓ Branch 12 taken 6 times.
6 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL, error);
158 6 }
159
160 //! Internal interface handler for directive_pushTlmValAndTime
161 3 void FpySequencer::directive_pushTlmValAndTime_internalInterfaceHandler(
162 const Svc::FpySequencer_PushTlmValAndTimeDirective& directive,
163 U64 sequenceIndex) {
164
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PUSH_TLM_VAL_AND_TIME, sequenceIndex)) {
165 2 return;
166 }
167
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
168
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->pushTlmValAndTime_directiveHandler(directive, error));
169
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL_AND_TIME, error);
170 1 }
171
172 //! Internal interface handler for directive_pushPrm
173 3 void FpySequencer::directive_pushPrm_internalInterfaceHandler(const Svc::FpySequencer_PushPrmDirective& directive,
174 U64 sequenceIndex) {
175
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PUSH_PRM, sequenceIndex)) {
176 2 return;
177 }
178
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
179
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->pushPrm_directiveHandler(directive, error));
180
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_PRM, error);
181 1 }
182
183 //! Internal interface handler for directive_constCmd
184 8 void FpySequencer::directive_constCmd_internalInterfaceHandler(const Svc::FpySequencer_ConstCmdDirective& directive,
185 U64 sequenceIndex) {
186
4/4
✓ Branch 5 taken 8 times.
✓ Branch 8 taken 8 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 4 times.
8 if (!this->checkDirectiveExpected(Fpy::DirectiveId::CONST_CMD, sequenceIndex)) {
187 4 return;
188 }
189
1/1
✓ Branch 2 taken 4 times.
4 DirectiveError error = DirectiveError::NO_ERROR;
190
2/2
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->sendSignal(this->constCmd_directiveHandler(directive, error));
191
3/3
✓ Branch 5 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 12 taken 4 times.
4 handleDirectiveErrorCode(Fpy::DirectiveId::CONST_CMD, error);
192 4 }
193
194 //! Internal interface handler for directive_stackOp
195 7 void FpySequencer::directive_stackOp_internalInterfaceHandler(const Svc::FpySequencer_StackOpDirective& directive,
196 U64 sequenceIndex) {
197
4/4
✓ Branch 9 taken 7 times.
✓ Branch 12 taken 7 times.
✓ Branch 17 taken 2 times.
✓ Branch 18 taken 5 times.
7 if (!this->checkDirectiveExpected(directive.get__op(), sequenceIndex)) {
198 2 return;
199 }
200
1/1
✓ Branch 2 taken 5 times.
5 DirectiveError error = DirectiveError::NO_ERROR;
201
2/2
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
5 this->sendSignal(this->stackOp_directiveHandler(directive, error));
202
3/3
✓ Branch 5 taken 5 times.
✓ Branch 13 taken 5 times.
✓ Branch 16 taken 5 times.
5 handleDirectiveErrorCode(directive.get__op(), error);
203 5 }
204
205 //! Internal interface handler for directive_exit
206 9 void FpySequencer::directive_exit_internalInterfaceHandler(const Svc::FpySequencer_ExitDirective& directive,
207 U64 sequenceIndex) {
208
4/4
✓ Branch 5 taken 9 times.
✓ Branch 8 taken 9 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 7 times.
9 if (!this->checkDirectiveExpected(Fpy::DirectiveId::EXIT, sequenceIndex)) {
209 2 return;
210 }
211
1/1
✓ Branch 2 taken 7 times.
7 DirectiveError error = DirectiveError::NO_ERROR;
212
2/2
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
7 this->sendSignal(this->exit_directiveHandler(directive, error));
213
3/3
✓ Branch 5 taken 7 times.
✓ Branch 9 taken 7 times.
✓ Branch 12 taken 7 times.
7 handleDirectiveErrorCode(Fpy::DirectiveId::EXIT, error);
214 7 }
215
216 //! Internal interface handler for directive_allocate
217 8 void FpySequencer::directive_allocate_internalInterfaceHandler(const Svc::FpySequencer_AllocateDirective& directive,
218 U64 sequenceIndex) {
219
4/4
✓ Branch 5 taken 8 times.
✓ Branch 8 taken 8 times.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 5 times.
8 if (!this->checkDirectiveExpected(Fpy::DirectiveId::ALLOCATE, sequenceIndex)) {
220 3 return;
221 }
222
1/1
✓ Branch 2 taken 5 times.
5 DirectiveError error = DirectiveError::NO_ERROR;
223
2/2
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
5 this->sendSignal(this->allocate_directiveHandler(directive, error));
224
3/3
✓ Branch 5 taken 5 times.
✓ Branch 9 taken 5 times.
✓ Branch 12 taken 5 times.
5 handleDirectiveErrorCode(Fpy::DirectiveId::ALLOCATE, error);
225 5 }
226
227 //! Internal interface handler for directive_storeRelConstOffset
228 3 void FpySequencer::directive_storeRelConstOffset_internalInterfaceHandler(
229 const Svc::FpySequencer_StoreRelConstOffsetDirective& directive,
230 U64 sequenceIndex) {
231
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::STORE_REL_CONST_OFFSET, sequenceIndex)) {
232 2 return;
233 }
234
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
235
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeRelConstOffset_directiveHandler(directive, error));
236
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL_CONST_OFFSET, error);
237 1 }
238
239 //! Internal interface handler for directive_pushVal
240 52 void FpySequencer::directive_pushVal_internalInterfaceHandler(const Svc::FpySequencer_PushValDirective& directive,
241 U64 sequenceIndex) {
242
4/4
✓ Branch 5 taken 52 times.
✓ Branch 8 taken 52 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 50 times.
52 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PUSH_VAL, sequenceIndex)) {
243 2 return;
244 }
245
1/1
✓ Branch 2 taken 50 times.
50 DirectiveError error = DirectiveError::NO_ERROR;
246
2/2
✓ Branch 7 taken 50 times.
✓ Branch 10 taken 50 times.
50 this->sendSignal(this->pushVal_directiveHandler(directive, error));
247
3/3
✓ Branch 5 taken 50 times.
✓ Branch 9 taken 50 times.
✓ Branch 12 taken 50 times.
50 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_VAL, error);
248 50 }
249
250 //! Internal interface handler for directive_loadRel
251 11 void FpySequencer::directive_loadRel_internalInterfaceHandler(const Svc::FpySequencer_LoadRelDirective& directive,
252 U64 sequenceIndex) {
253
4/4
✓ Branch 5 taken 11 times.
✓ Branch 8 taken 11 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 9 times.
11 if (!this->checkDirectiveExpected(Fpy::DirectiveId::LOAD_REL, sequenceIndex)) {
254 2 return;
255 }
256
1/1
✓ Branch 2 taken 9 times.
9 DirectiveError error = DirectiveError::NO_ERROR;
257
2/2
✓ Branch 7 taken 9 times.
✓ Branch 10 taken 9 times.
9 this->sendSignal(this->loadRel_directiveHandler(directive, error));
258
3/3
✓ Branch 5 taken 9 times.
✓ Branch 9 taken 9 times.
✓ Branch 12 taken 9 times.
9 handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_REL, error);
259 9 }
260
261 //! Internal interface handler for directive_discard
262 20 void FpySequencer::directive_discard_internalInterfaceHandler(const Svc::FpySequencer_DiscardDirective& directive,
263 U64 sequenceIndex) {
264
4/4
✓ Branch 5 taken 20 times.
✓ Branch 8 taken 20 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 18 times.
20 if (!this->checkDirectiveExpected(Fpy::DirectiveId::DISCARD, sequenceIndex)) {
265 2 return;
266 }
267
1/1
✓ Branch 2 taken 18 times.
18 DirectiveError error = DirectiveError::NO_ERROR;
268
2/2
✓ Branch 7 taken 18 times.
✓ Branch 10 taken 18 times.
18 this->sendSignal(this->discard_directiveHandler(directive, error));
269
3/3
✓ Branch 5 taken 18 times.
✓ Branch 9 taken 18 times.
✓ Branch 12 taken 18 times.
18 handleDirectiveErrorCode(Fpy::DirectiveId::DISCARD, error);
270 18 }
271
272 //! Internal interface handler for directive_memCmp
273 3 void FpySequencer::directive_memCmp_internalInterfaceHandler(const Svc::FpySequencer_MemCmpDirective& directive,
274 U64 sequenceIndex) {
275
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::MEMCMP, sequenceIndex)) {
276 2 return;
277 }
278
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
279
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->memCmp_directiveHandler(directive, error));
280
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::MEMCMP, error);
281 1 }
282
283 //! Internal interface handler for directive_stackCmd
284 3 void FpySequencer::directive_stackCmd_internalInterfaceHandler(const Svc::FpySequencer_StackCmdDirective& directive,
285 U64 sequenceIndex) {
286
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::STACK_CMD, sequenceIndex)) {
287 2 return;
288 }
289
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
290
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->stackCmd_directiveHandler(directive, error));
291
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STACK_CMD, error);
292 1 }
293
294 //! Internal interface handler for directive_pushTime
295 3 void FpySequencer::directive_pushTime_internalInterfaceHandler(const Svc::FpySequencer_PushTimeDirective& directive,
296 U64 sequenceIndex) {
297
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PUSH_TIME, sequenceIndex)) {
298 2 return;
299 }
300
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
301
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->pushTime_directiveHandler(directive, error));
302
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TIME, error);
303 1 }
304
305 //! Internal interface handler for directive_setSeed
306 3 void FpySequencer::directive_setSeed_internalInterfaceHandler(const Svc::FpySequencer_SetSeedDirective& directive,
307 U64 sequenceIndex) {
308
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::SET_SEED, sequenceIndex)) {
309 2 return;
310 }
311
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
312
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->setSeed_directiveHandler(directive, error));
313
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::SET_SEED, error);
314 1 }
315
316 //! Internal interface handler for directive_pushRand
317 4 void FpySequencer::directive_pushRand_internalInterfaceHandler(const Svc::FpySequencer_PushRandDirective& directive,
318 U64 sequenceIndex) {
319
4/4
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 4 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 2 times.
4 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PUSH_RAND, sequenceIndex)) {
320 2 return;
321 }
322
1/1
✓ Branch 2 taken 2 times.
2 DirectiveError error = DirectiveError::NO_ERROR;
323
2/2
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->sendSignal(this->pushRand_directiveHandler(directive, error));
324
3/3
✓ Branch 5 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 2 times.
2 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_RAND, error);
325 2 }
326
327 //! Internal interface handler for directive_getField
328 3 void FpySequencer::directive_getField_internalInterfaceHandler(const Svc::FpySequencer_GetFieldDirective& directive,
329 U64 sequenceIndex) {
330
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::GET_FIELD, sequenceIndex)) {
331 2 return;
332 }
333
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
334
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->getField_directiveHandler(directive, error));
335
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::GET_FIELD, error);
336 1 }
337
338 //! Internal interface handler for directive_peek
339 3 void FpySequencer::directive_peek_internalInterfaceHandler(const Svc::FpySequencer_PeekDirective& directive,
340 U64 sequenceIndex) {
341
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::PEEK, sequenceIndex)) {
342 2 return;
343 }
344
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
345
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->peek_directiveHandler(directive, error));
346
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PEEK, error);
347 1 }
348
349 //! Internal interface handler for directive_storeRel
350 3 void FpySequencer::directive_storeRel_internalInterfaceHandler(const Svc::FpySequencer_StoreRelDirective& directive,
351 U64 sequenceIndex) {
352
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::STORE_REL, sequenceIndex)) {
353 2 return;
354 }
355
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
356
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeRel_directiveHandler(directive, error));
357
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL, error);
358 1 }
359
360 //! Internal interface handler for directive_call
361 3 void FpySequencer::directive_call_internalInterfaceHandler(const Svc::FpySequencer_CallDirective& directive,
362 U64 sequenceIndex) {
363
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::CALL, sequenceIndex)) {
364 2 return;
365 }
366
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
367
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->call_directiveHandler(directive, error));
368
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::CALL, error);
369 1 }
370
371 //! Internal interface handler for directive_return
372 3 void FpySequencer::directive_return_internalInterfaceHandler(const Svc::FpySequencer_ReturnDirective& directive,
373 U64 sequenceIndex) {
374
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::RETURN, sequenceIndex)) {
375 2 return;
376 }
377
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
378
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->return_directiveHandler(directive, error));
379
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::RETURN, error);
380 1 }
381
382 //! Internal interface handler for directive_loadAbs
383 3 void FpySequencer::directive_loadAbs_internalInterfaceHandler(const Svc::FpySequencer_LoadAbsDirective& directive,
384 U64 sequenceIndex) {
385
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::LOAD_ABS, sequenceIndex)) {
386 2 return;
387 }
388
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
389
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->loadAbs_directiveHandler(directive, error));
390
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_ABS, error);
391 1 }
392
393 //! Internal interface handler for directive_storeAbs
394 3 void FpySequencer::directive_storeAbs_internalInterfaceHandler(const Svc::FpySequencer_StoreAbsDirective& directive,
395 U64 sequenceIndex) {
396
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::STORE_ABS, sequenceIndex)) {
397 2 return;
398 }
399
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
400
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeAbs_directiveHandler(directive, error));
401
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS, error);
402 1 }
403
404 //! Internal interface handler for directive_storeAbsConstOffset
405 3 void FpySequencer::directive_storeAbsConstOffset_internalInterfaceHandler(
406 const Svc::FpySequencer_StoreAbsConstOffsetDirective& directive,
407 U64 sequenceIndex) {
408
4/4
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
3 if (!this->checkDirectiveExpected(Fpy::DirectiveId::STORE_ABS_CONST_OFFSET, sequenceIndex)) {
409 2 return;
410 }
411
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
412
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeAbsConstOffset_directiveHandler(directive, error));
413
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS_CONST_OFFSET, error);
414 1 }
415
416 //! Internal interface handler for directive_popEvent
417 2 void FpySequencer::directive_popEvent_internalInterfaceHandler(const Svc::FpySequencer_PopEventDirective& directive,
418 U64 sequenceIndex) {
419
3/4
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
2 if (!this->checkDirectiveExpected(Fpy::DirectiveId::POP_EVENT, sequenceIndex)) {
420 2 return;
421 }
422 ✗ DirectiveError error = DirectiveError::NO_ERROR;
423 ✗ this->sendSignal(this->popEvent_directiveHandler(directive, error));
424 ✗ handleDirectiveErrorCode(Fpy::DirectiveId::POP_EVENT, error);
425 ✗ }
426
427 //! Internal interface handler for directive_popSerializable
428 2 void FpySequencer::directive_popSerializable_internalInterfaceHandler(
429 const Svc::FpySequencer_PopSerializableDirective& directive,
430 U64 sequenceIndex) {
431
3/4
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
2 if (!this->checkDirectiveExpected(Fpy::DirectiveId::POP_SERIALIZABLE, sequenceIndex)) {
432 2 return;
433 }
434 ✗ DirectiveError error = DirectiveError::NO_ERROR;
435 ✗ this->sendSignal(this->popSerializable_directiveHandler(directive, error));
436 ✗ handleDirectiveErrorCode(Fpy::DirectiveId::POP_SERIALIZABLE, error);
437 ✗ }
438
439 //! Internal interface handler for directive_waitRel
440 6 Signal FpySequencer::waitRel_directiveHandler(const FpySequencer_WaitRelDirective& directive, DirectiveError& error) {
441
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (this->m_runtime.stack.size < 8) {
442 ✗ error = DirectiveError::STACK_UNDERFLOW;
443 ✗ return Signal::stmtResponse_failure;
444 }
445
446
1/1
✓ Branch 3 taken 6 times.
6 Fw::Time wakeupTime = this->getTime();
447
448
1/1
✓ Branch 4 taken 6 times.
6 U32 uSeconds = this->m_runtime.stack.pop<U32>();
449
1/1
✓ Branch 4 taken 6 times.
6 U32 seconds = this->m_runtime.stack.pop<U32>();
450
451
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (uSeconds >= 1000000) {
452
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::INVALID_ARG;
453 1 return Signal::stmtResponse_failure;
454 }
455
456
1/1
✓ Branch 2 taken 5 times.
5 wakeupTime.add(seconds, uSeconds);
457
1/1
✓ Branch 6 taken 5 times.
5 this->m_runtime.wakeupTime = wakeupTime;
458 5 return Signal::stmtResponse_beginSleep;
459 6 }
460
461 //! Internal interface handler for directive_waitAbs
462 3 Signal FpySequencer::waitAbs_directiveHandler(const FpySequencer_WaitAbsDirective& directive, DirectiveError& error) {
463
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (this->m_runtime.stack.size < 2 * sizeof(U32) + sizeof(FwTimeContextStoreType) + sizeof(FwTimeBaseStoreType)) {
464 ✗ error = DirectiveError::STACK_UNDERFLOW;
465 ✗ return Signal::stmtResponse_failure;
466 }
467
468 3 U32 uSeconds = this->m_runtime.stack.pop<U32>();
469 3 U32 seconds = this->m_runtime.stack.pop<U32>();
470 3 FwTimeContextStoreType ctx = this->m_runtime.stack.pop<FwTimeContextStoreType>();
471 3 FwTimeBaseStoreType base = this->m_runtime.stack.pop<FwTimeBaseStoreType>();
472
473
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (uSeconds >= 1000000) {
474 1 error = DirectiveError::INVALID_ARG;
475 1 return Signal::stmtResponse_failure;
476 }
477
478
3/3
✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 15 taken 2 times.
2 this->m_runtime.wakeupTime = Fw::Time(static_cast<TimeBase::T>(base), ctx, seconds, uSeconds);
479 2 return Signal::stmtResponse_beginSleep;
480 }
481
482 //! Internal interface handler for directive_goto
483 5 Signal FpySequencer::goto_directiveHandler(const FpySequencer_GotoDirective& directive, DirectiveError& error) {
484 // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
485
2/2
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 4 times.
5 if (directive.get_statementIndex() > m_sequenceObj.get_header().get_statementCount()) {
486 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
487 1 return Signal::stmtResponse_failure;
488 }
489 4 m_runtime.nextStatementIndex = directive.get_statementIndex();
490 4 return Signal::stmtResponse_success;
491 }
492
493 //! Internal interface handler for directive_if
494 12 Signal FpySequencer::if_directiveHandler(const FpySequencer_IfDirective& directive, DirectiveError& error) {
495
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 11 times.
12 if (this->m_runtime.stack.size < 1) {
496 1 error = DirectiveError::STACK_UNDERFLOW;
497 1 return Signal::stmtResponse_failure;
498 }
499 // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
500
2/2
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 10 times.
11 if (directive.get_falseGotoStmtIndex() > m_sequenceObj.get_header().get_statementCount()) {
501 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
502 1 return Signal::stmtResponse_failure;
503 }
504
505
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 6 times.
10 if (this->m_runtime.stack.pop<U8>() != 0) {
506 // proceed to next instruction
507 4 return Signal::stmtResponse_success;
508 }
509
510 // conditional false case
511 6 this->m_runtime.nextStatementIndex = directive.get_falseGotoStmtIndex();
512 6 return Signal::stmtResponse_success;
513 }
514
515 2058 Signal FpySequencer::noOp_directiveHandler(const FpySequencer_NoOpDirective& directive, DirectiveError& error) {
516 2058 return Signal::stmtResponse_success;
517 }
518
519 10 Signal FpySequencer::pushTlmVal_directiveHandler(const FpySequencer_PushTlmValDirective& directive,
520 DirectiveError& error) {
521
2/3
✓ Branch 5 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
10 if (!this->isConnected_getTlmChan_OutputPort(0)) {
522 ✗ error = DirectiveError::TLM_GET_NOT_CONNECTED;
523 ✗ return Signal::stmtResponse_failure;
524 }
525
1/1
✓ Branch 2 taken 10 times.
10 Fw::Time tlmTime;
526
1/1
✓ Branch 2 taken 10 times.
10 Fw::TlmBuffer tlmValue;
527
1/1
✓ Branch 7 taken 10 times.
10 Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
528
529
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 9 times.
10 if (valid != Fw::TlmValid::VALID) {
530 // could not find this tlm chan
531
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::TLM_CHAN_NOT_FOUND;
532 1 return Signal::stmtResponse_failure;
533 }
534
535
3/3
✓ Branch 2 taken 9 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 8 times.
9 if (tlmValue.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
536
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
537 1 return Signal::stmtResponse_failure;
538 }
539
2/2
✓ Branch 5 taken 8 times.
✓ Branch 10 taken 8 times.
8 this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
540 8 return Signal::stmtResponse_success;
541 10 }
542
543 6 Signal FpySequencer::pushTlmValAndTime_directiveHandler(const FpySequencer_PushTlmValAndTimeDirective& directive,
544 DirectiveError& error) {
545
2/3
✓ Branch 5 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
6 if (!this->isConnected_getTlmChan_OutputPort(0)) {
546 ✗ error = DirectiveError::TLM_GET_NOT_CONNECTED;
547 ✗ return Signal::stmtResponse_failure;
548 }
549
550
1/1
✓ Branch 2 taken 6 times.
6 Fw::Time tlmTime;
551
1/1
✓ Branch 2 taken 6 times.
6 Fw::TlmBuffer tlmValue;
552
1/1
✓ Branch 7 taken 6 times.
6 Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
553
554
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
6 if (valid != Fw::TlmValid::VALID) {
555 // could not find this tlm chan
556
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::TLM_CHAN_NOT_FOUND;
557 1 return Signal::stmtResponse_failure;
558 }
559
560 5 U8 tlmTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
561
1/1
✓ Branch 2 taken 5 times.
5 Fw::ExternalSerializeBuffer timeEsb(tlmTimeBuf, Fw::Time::SERIALIZED_SIZE);
562
1/1
✓ Branch 2 taken 5 times.
5 Fw::SerializeStatus stat = timeEsb.serializeFrom(tlmTime);
563
564 // coding error if this failed, we should have enough space
565 5 FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
566
567 // check that our stack won't overflow if we put both val and time on it
568
4/4
✓ Branch 2 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 4 times.
5 if (tlmValue.getSize() + timeEsb.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
569
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
570 1 return Signal::stmtResponse_failure;
571 }
572
573 // push tlm to end of stack
574
2/2
✓ Branch 5 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
575 // now push time to end of stack
576
2/2
✓ Branch 5 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
577 4 return Signal::stmtResponse_success;
578 6 }
579
580 6 Signal FpySequencer::pushPrm_directiveHandler(const FpySequencer_PushPrmDirective& directive, DirectiveError& error) {
581
3/3
✓ Branch 5 taken 6 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 5 times.
6 if (!this->isConnected_getParam_OutputPort(0)) {
582
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::PRM_GET_NOT_CONNECTED;
583 1 return Signal::stmtResponse_failure;
584 }
585
586
1/1
✓ Branch 2 taken 5 times.
5 Fw::ParamBuffer prmValue;
587
1/1
✓ Branch 7 taken 5 times.
5 Fw::ParamValid valid = this->getParam_out(0, directive.get_prmId(), prmValue);
588
589
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (valid != Fw::ParamValid::VALID) {
590 // could not find this prm in the DB
591
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::PRM_NOT_FOUND;
592 1 return Signal::stmtResponse_failure;
593 }
594
595
3/3
✓ Branch 2 taken 4 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 3 times.
4 if (prmValue.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
596
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
597 1 return Signal::stmtResponse_failure;
598 }
599
600
2/2
✓ Branch 5 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->m_runtime.stack.push(prmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(prmValue.getSize()));
601 3 return Signal::stmtResponse_success;
602 5 }
603
604 9 Signal FpySequencer::constCmd_directiveHandler(const FpySequencer_ConstCmdDirective& directive, DirectiveError& error) {
605 // the cmd response code will be pushed to the stack when it comes back, so make sure
606 // there is room for it now, before the cmd is dispatched
607
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 8 times.
9 if (sizeof(Fw::CmdResponse::SerialType) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
608 1 error = DirectiveError::STACK_OVERFLOW;
609 1 return Signal::stmtResponse_failure;
610 }
611
2/3
✓ Branch 14 taken 8 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 8 times.
8 if (this->sendCmd(directive.get_opCode(), directive.get_argBuf(), directive.get__argBufSize()) ==
612 Fw::Success::FAILURE) {
613 ✗ return Signal::stmtResponse_failure;
614 } else {
615 // now tell the SM to wait some more until we get the cmd response back
616 // if we've already got the response back this should be harmless
617 8 return Signal::stmtResponse_keepWaiting;
618 }
619 }
620
621 8 DirectiveError FpySequencer::op_or() {
622
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (this->m_runtime.stack.size < sizeof(U8) * 2) {
623 2 return DirectiveError::STACK_UNDERFLOW;
624 }
625 6 this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() | this->m_runtime.stack.pop<U8>()));
626 6 return DirectiveError::NO_ERROR;
627 }
628 6 DirectiveError FpySequencer::op_and() {
629
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U8) * 2) {
630 2 return DirectiveError::STACK_UNDERFLOW;
631 }
632 4 this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() & this->m_runtime.stack.pop<U8>()));
633 4 return DirectiveError::NO_ERROR;
634 }
635 6 DirectiveError FpySequencer::op_ieq() {
636
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
6 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
637 3 return DirectiveError::STACK_UNDERFLOW;
638 }
639
2/2
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() == this->m_runtime.stack.pop<I64>())
640 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
641 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
642 3 return DirectiveError::NO_ERROR;
643 }
644 5 DirectiveError FpySequencer::op_ine() {
645
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
646 2 return DirectiveError::STACK_UNDERFLOW;
647 }
648
2/2
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() != this->m_runtime.stack.pop<I64>())
649 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
650 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
651 3 return DirectiveError::NO_ERROR;
652 }
653 6 DirectiveError FpySequencer::op_ult() {
654
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
655 2 return DirectiveError::STACK_UNDERFLOW;
656 }
657 4 U64 rhs = this->m_runtime.stack.pop<U64>();
658 4 U64 lhs = this->m_runtime.stack.pop<U64>();
659
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
660 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
661 4 return DirectiveError::NO_ERROR;
662 }
663 6 DirectiveError FpySequencer::op_ule() {
664
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
665 2 return DirectiveError::STACK_UNDERFLOW;
666 }
667 4 U64 rhs = this->m_runtime.stack.pop<U64>();
668 4 U64 lhs = this->m_runtime.stack.pop<U64>();
669
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
670 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
671 4 return DirectiveError::NO_ERROR;
672 }
673 6 DirectiveError FpySequencer::op_ugt() {
674
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
675 2 return DirectiveError::STACK_UNDERFLOW;
676 }
677 4 U64 rhs = this->m_runtime.stack.pop<U64>();
678 4 U64 lhs = this->m_runtime.stack.pop<U64>();
679
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
680 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
681 4 return DirectiveError::NO_ERROR;
682 }
683 8 DirectiveError FpySequencer::op_uge() {
684
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
685 2 return DirectiveError::STACK_UNDERFLOW;
686 }
687 6 U64 rhs = this->m_runtime.stack.pop<U64>();
688 6 U64 lhs = this->m_runtime.stack.pop<U64>();
689
2/2
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
6 this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
690 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
691 6 return DirectiveError::NO_ERROR;
692 }
693 6 DirectiveError FpySequencer::op_slt() {
694
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
695 2 return DirectiveError::STACK_UNDERFLOW;
696 }
697 4 I64 rhs = this->m_runtime.stack.pop<I64>();
698 4 I64 lhs = this->m_runtime.stack.pop<I64>();
699
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
700 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
701 4 return DirectiveError::NO_ERROR;
702 }
703 5 DirectiveError FpySequencer::op_sle() {
704
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
705 2 return DirectiveError::STACK_UNDERFLOW;
706 }
707 3 I64 rhs = this->m_runtime.stack.pop<I64>();
708 3 I64 lhs = this->m_runtime.stack.pop<I64>();
709
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
710 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
711 3 return DirectiveError::NO_ERROR;
712 }
713 5 DirectiveError FpySequencer::op_sgt() {
714
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
715 2 return DirectiveError::STACK_UNDERFLOW;
716 }
717 3 I64 rhs = this->m_runtime.stack.pop<I64>();
718 3 I64 lhs = this->m_runtime.stack.pop<I64>();
719
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
3 this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
720 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
721 3 return DirectiveError::NO_ERROR;
722 }
723 5 DirectiveError FpySequencer::op_sge() {
724
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
725 2 return DirectiveError::STACK_UNDERFLOW;
726 }
727 3 I64 rhs = this->m_runtime.stack.pop<I64>();
728 3 I64 lhs = this->m_runtime.stack.pop<I64>();
729
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
730 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
731 3 return DirectiveError::NO_ERROR;
732 }
733 6 DirectiveError FpySequencer::op_feq() {
734
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
735 2 return DirectiveError::STACK_UNDERFLOW;
736 }
737 4 F64 rhs = this->m_runtime.stack.pop<F64>();
738 4 F64 lhs = this->m_runtime.stack.pop<F64>();
739 // eq is true if they are equal and neither is nan
740
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 3 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs == rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
741 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
742 4 return DirectiveError::NO_ERROR;
743 }
744 6 DirectiveError FpySequencer::op_fne() {
745
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
746 2 return DirectiveError::STACK_UNDERFLOW;
747 }
748 4 F64 rhs = this->m_runtime.stack.pop<F64>();
749 4 F64 lhs = this->m_runtime.stack.pop<F64>();
750 // ne is true if they are not equal or either is nan
751
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs != rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
752 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
753 4 return DirectiveError::NO_ERROR;
754 }
755 7 DirectiveError FpySequencer::op_flt() {
756
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
757 2 return DirectiveError::STACK_UNDERFLOW;
758 }
759 5 F64 rhs = this->m_runtime.stack.pop<F64>();
760 5 F64 lhs = this->m_runtime.stack.pop<F64>();
761
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 this->m_runtime.stack.push(static_cast<U8>(std::isless(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
762 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
763 5 return DirectiveError::NO_ERROR;
764 }
765 6 DirectiveError FpySequencer::op_fle() {
766
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
767 2 return DirectiveError::STACK_UNDERFLOW;
768 }
769 4 F64 rhs = this->m_runtime.stack.pop<F64>();
770 4 F64 lhs = this->m_runtime.stack.pop<F64>();
771
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 this->m_runtime.stack.push(static_cast<U8>(std::islessequal(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
772 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
773 4 return DirectiveError::NO_ERROR;
774 }
775 6 DirectiveError FpySequencer::op_fgt() {
776
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
777 2 return DirectiveError::STACK_UNDERFLOW;
778 }
779 4 F64 rhs = this->m_runtime.stack.pop<F64>();
780 4 F64 lhs = this->m_runtime.stack.pop<F64>();
781
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 this->m_runtime.stack.push(static_cast<U8>(std::isgreater(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
782 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
783 4 return DirectiveError::NO_ERROR;
784 }
785 7 DirectiveError FpySequencer::op_fge() {
786
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
787 2 return DirectiveError::STACK_UNDERFLOW;
788 }
789 5 F64 rhs = this->m_runtime.stack.pop<F64>();
790 5 F64 lhs = this->m_runtime.stack.pop<F64>();
791
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 2 times.
5 this->m_runtime.stack.push(static_cast<U8>(std::isgreaterequal(lhs, rhs)
792 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
793 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
794 5 return DirectiveError::NO_ERROR;
795 }
796 3 DirectiveError FpySequencer::op_not() {
797
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U8)) {
798 1 return DirectiveError::STACK_UNDERFLOW;
799 }
800
1/2
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
2 this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<U8>() == 0)
801 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
802 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
803 2 return DirectiveError::NO_ERROR;
804 }
805 4 DirectiveError FpySequencer::op_fpext() {
806 // convert F32 to F64
807
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(F32)) {
808 1 return DirectiveError::STACK_UNDERFLOW;
809 }
810 // the result is wider than the operand, so it needs room to grow
811
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (sizeof(F64) - sizeof(F32) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
812 1 return DirectiveError::STACK_OVERFLOW;
813 }
814 2 this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<F32>()));
815 2 return DirectiveError::NO_ERROR;
816 }
817 2 DirectiveError FpySequencer::op_fptrunc() {
818 // convert F64 to F32
819
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(F64)) {
820 1 return DirectiveError::STACK_UNDERFLOW;
821 }
822 1 this->m_runtime.stack.push(static_cast<F32>(this->m_runtime.stack.pop<F64>()));
823 1 return DirectiveError::NO_ERROR;
824 }
825 8 DirectiveError FpySequencer::op_fptosi() {
826
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 7 times.
8 if (this->m_runtime.stack.size < sizeof(F64)) {
827 1 return DirectiveError::STACK_UNDERFLOW;
828 }
829 7 F64 val = this->m_runtime.stack.pop<F64>();
830 // NaN -> 0, out-of-range clamps, in-range truncates toward
831 // zero. The raw static_cast is UB for NaN and out-of-range values.
832 // 2^63 is exactly representable as F64 and is one past I64 max; -2^63
833 // is exactly I64 min and in range.
834 7 const F64 bound = std::ldexp(1.0, 63);
835 I64 result;
836
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
7 if (std::isnan(val)) {
837 1 result = 0;
838
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 } else if (val >= bound) {
839 2 result = std::numeric_limits<I64>::max();
840
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 } else if (val < -bound) {
841 2 result = std::numeric_limits<I64>::min();
842 } else {
843 2 result = static_cast<I64>(val);
844 }
845 7 this->m_runtime.stack.push(result);
846 7 return DirectiveError::NO_ERROR;
847 }
848 2 DirectiveError FpySequencer::op_sitofp() {
849
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(I64)) {
850 1 return DirectiveError::STACK_UNDERFLOW;
851 }
852 1 this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<I64>()));
853 1 return DirectiveError::NO_ERROR;
854 }
855 7 DirectiveError FpySequencer::op_fptoui() {
856
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (this->m_runtime.stack.size < sizeof(F64)) {
857 1 return DirectiveError::STACK_UNDERFLOW;
858 }
859 6 F64 val = this->m_runtime.stack.pop<F64>();
860 // NaN -> 0, negatives truncate to at most 0 and clamp there,
861 // 2^64 (one past U64 max) and above clamp to U64 max. The raw
862 // static_cast is UB for NaN and out-of-range values.
863 6 const F64 bound = std::ldexp(1.0, 64);
864 U64 result;
865
6/6
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 3 times.
6 if (std::isnan(val) || val < 0.0) {
866 3 result = 0;
867
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 } else if (val >= bound) {
868 2 result = std::numeric_limits<U64>::max();
869 } else {
870 1 result = static_cast<U64>(val);
871 }
872 6 this->m_runtime.stack.push(result);
873 6 return DirectiveError::NO_ERROR;
874 }
875 2 DirectiveError FpySequencer::op_uitofp() {
876
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(U64)) {
877 1 return DirectiveError::STACK_UNDERFLOW;
878 }
879 1 this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<U64>()));
880 1 return DirectiveError::NO_ERROR;
881 }
882 5 DirectiveError FpySequencer::op_add() {
883
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
884 2 return DirectiveError::STACK_UNDERFLOW;
885 }
886 3 I64 rhs = this->m_runtime.stack.pop<I64>();
887 3 I64 lhs = this->m_runtime.stack.pop<I64>();
888 // Check for overflow and underflow and return the appropriate error code
889 // Overflow can only occur with both operands positive and occurs when one operand is greater than the maximum value
890 // less the other operand. If either operand is negative or zero, overflow cannot occur.
891
7/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
3 if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() - rhs) < lhs)) {
892 1 return DirectiveError::ARITHMETIC_OVERFLOW;
893 }
894 // Underflow can only occur with both operands negative and occurs when one operand is less than the minimum value
895 // minus the other operand. If either operand is positive or zero, underflow cannot occur.
896
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
2 else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() - rhs) > lhs)) {
897 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
898 }
899 1 this->m_runtime.stack.push(static_cast<I64>(lhs + rhs));
900 1 return DirectiveError::NO_ERROR;
901 }
902 5 DirectiveError FpySequencer::op_sub() {
903
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
904 2 return DirectiveError::STACK_UNDERFLOW;
905 }
906 3 I64 rhs = this->m_runtime.stack.pop<I64>();
907 3 I64 lhs = this->m_runtime.stack.pop<I64>();
908 // Check for overflow and underflow and return the appropriate error code
909 // Overflow can only occur when the left operand is positive and the right operand is negative. It occurs when the
910 // left (positive) operand is greater than the maximum value plus the other (negative) operand. If the right
911 // operand is positive or zero, overflow cannot occur.
912
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
3 if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() + rhs) < lhs)) {
913 1 return DirectiveError::ARITHMETIC_OVERFLOW;
914 }
915 // Underflow can only occur when the left operand is negative and the right operand is positive. It occurs when the
916 // left (negative) operand is less than the minimum value plus the other (positive) operand. If the right operand
917 // is negative or zero, underflow cannot occur.
918
6/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
2 else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() + rhs) > lhs)) {
919 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
920 }
921 1 this->m_runtime.stack.push(static_cast<I64>(lhs - rhs));
922 1 return DirectiveError::NO_ERROR;
923 }
924 15 DirectiveError FpySequencer::op_mul() {
925
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 13 times.
15 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
926 2 return DirectiveError::STACK_UNDERFLOW;
927 }
928 13 I64 rhs = this->m_runtime.stack.pop<I64>();
929 13 I64 lhs = this->m_runtime.stack.pop<I64>();
930 // Check for overflow and underflow and return the appropriate error code
931 // Overflow can only occur with operands of matching signs and occurs when one operand is greater (or less) than the
932 // maximum value divided by the other operand. Either operand being zero precludes overflow.
933 // Check the both positive case.
934
9/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 12 times.
13 if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() / rhs) < lhs)) {
935 1 return DirectiveError::ARITHMETIC_OVERFLOW;
936 }
937 // Check the both negative case. Compare without negation: negating a value of min is undefined behavior
938
9/10
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 5 times.
12 else if ((rhs < 0) && (lhs < 0) && (lhs < (std::numeric_limits<I64>::max() / rhs))) {
939 7 return DirectiveError::ARITHMETIC_OVERFLOW;
940 }
941 // Underflow can occur with operands of differing signs and occurs when one operand is less than the minimum value
942 // divided by the other operand. Either operand being zero precludes underflow.
943 // Check the case where lhs is positive.
944
8/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 4 times.
5 else if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::min() / lhs) > rhs)) {
945 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
946 }
947 // Check the case where rhs is positive.
948
8/10
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 3 times.
4 else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() / rhs) > lhs)) {
949 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
950 }
951 3 this->m_runtime.stack.push(static_cast<I64>(lhs * rhs));
952 3 return DirectiveError::NO_ERROR;
953 }
954 4 DirectiveError FpySequencer::op_udiv() {
955
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
956 2 return DirectiveError::STACK_UNDERFLOW;
957 }
958 2 U64 rhs = this->m_runtime.stack.pop<U64>();
959 2 U64 lhs = this->m_runtime.stack.pop<U64>();
960 // Prevent division by zero
961
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (rhs == 0) {
962 1 return DirectiveError::DOMAIN_ERROR;
963 }
964
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 this->m_runtime.stack.push(static_cast<U64>(lhs / rhs));
965 1 return DirectiveError::NO_ERROR;
966 }
967 9 DirectiveError FpySequencer::op_sdiv() {
968
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 7 times.
9 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
969 2 return DirectiveError::STACK_UNDERFLOW;
970 }
971
972 7 I64 rhs = this->m_runtime.stack.pop<I64>();
973 7 I64 lhs = this->m_runtime.stack.pop<I64>();
974 // Prevent division by zero
975
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (rhs == 0) {
976 1 return DirectiveError::DOMAIN_ERROR;
977 }
978 // The one signed division that can overflow: |I64 min / -1| = 2^63 is not
979 // representable (and the C++ expression is UB, SIGFPE on x86)
980
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 5 times.
6 if ((lhs == std::numeric_limits<I64>::min()) && (rhs == -1)) {
981 1 return DirectiveError::ARITHMETIC_OVERFLOW;
982 }
983 // C++ / truncates toward zero; adjust to match Python's floored division:
984 // an inexact quotient with differing operand signs floors one below the
985 // truncated result. This mirrors op_smod.
986
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 I64 quotient = lhs / rhs;
987
7/8
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3 times.
5 if (((lhs % rhs) != 0) && ((lhs < 0) != (rhs < 0))) {
988 2 quotient -= 1;
989 }
990 5 this->m_runtime.stack.push(quotient);
991 5 return DirectiveError::NO_ERROR;
992 }
993 4 DirectiveError FpySequencer::op_umod() {
994
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
995 2 return DirectiveError::STACK_UNDERFLOW;
996 }
997 2 U64 rhs = this->m_runtime.stack.pop<U64>();
998
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (rhs == 0) {
999 1 return DirectiveError::DOMAIN_ERROR;
1000 }
1001 1 U64 lhs = this->m_runtime.stack.pop<U64>();
1002
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 this->m_runtime.stack.push(static_cast<U64>(lhs % rhs));
1003 1 return DirectiveError::NO_ERROR;
1004 }
1005 7 DirectiveError FpySequencer::op_smod() {
1006
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
1007 2 return DirectiveError::STACK_UNDERFLOW;
1008 }
1009 5 I64 rhs = this->m_runtime.stack.pop<I64>();
1010
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (rhs == 0) {
1011 1 return DirectiveError::DOMAIN_ERROR;
1012 }
1013 4 I64 lhs = this->m_runtime.stack.pop<I64>();
1014 // I64 min % -1 is 0, the mathematical remainder (matching wasm i64.rem_s),
1015 // but the C++ expression is UB (SIGFPE on x86) so it must be special-cased
1016
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
4 if ((lhs == std::numeric_limits<I64>::min()) && (rhs == -1)) {
1017 1 this->m_runtime.stack.push(static_cast<I64>(0));
1018 1 return DirectiveError::NO_ERROR;
1019 }
1020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 I64 res = static_cast<I64>(lhs % rhs);
1021 // in order to match Python's behavior,
1022 // if the signs of the remainder and divisor differ, adjust the result.
1023 // this happens when the result should be positive but is negative, or vice-versa.
1024 // credit Gemini 2.5 pro
1025
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
3 if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
1026 2 res += rhs;
1027 }
1028 3 this->m_runtime.stack.push(res);
1029 3 return DirectiveError::NO_ERROR;
1030 }
1031 3 DirectiveError FpySequencer::op_fadd() {
1032
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
1033 2 return DirectiveError::STACK_UNDERFLOW;
1034 }
1035 1 F64 rhs = this->m_runtime.stack.pop<F64>();
1036 1 F64 lhs = this->m_runtime.stack.pop<F64>();
1037 1 this->m_runtime.stack.push(static_cast<F64>(lhs + rhs));
1038 1 return DirectiveError::NO_ERROR;
1039 }
1040 3 DirectiveError FpySequencer::op_fsub() {
1041
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
1042 2 return DirectiveError::STACK_UNDERFLOW;
1043 }
1044 1 F64 rhs = this->m_runtime.stack.pop<F64>();
1045 1 F64 lhs = this->m_runtime.stack.pop<F64>();
1046 1 this->m_runtime.stack.push(static_cast<F64>(lhs - rhs));
1047 1 return DirectiveError::NO_ERROR;
1048 }
1049 3 DirectiveError FpySequencer::op_fmul() {
1050
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
1051 2 return DirectiveError::STACK_UNDERFLOW;
1052 }
1053 1 F64 rhs = this->m_runtime.stack.pop<F64>();
1054 1 F64 lhs = this->m_runtime.stack.pop<F64>();
1055 1 this->m_runtime.stack.push(static_cast<F64>(lhs * rhs));
1056 1 return DirectiveError::NO_ERROR;
1057 }
1058 3 DirectiveError FpySequencer::op_fdiv() {
1059
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
1060 2 return DirectiveError::STACK_UNDERFLOW;
1061 }
1062 1 F64 rhs = this->m_runtime.stack.pop<F64>();
1063 1 F64 lhs = this->m_runtime.stack.pop<F64>();
1064 1 this->m_runtime.stack.push(static_cast<F64>(lhs / rhs));
1065 1 return DirectiveError::NO_ERROR;
1066 }
1067 3 DirectiveError FpySequencer::op_fpow() {
1068
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
1069 2 return DirectiveError::STACK_UNDERFLOW;
1070 }
1071 1 F64 rhs = this->m_runtime.stack.pop<F64>();
1072 1 F64 lhs = this->m_runtime.stack.pop<F64>();
1073 1 this->m_runtime.stack.push(static_cast<F64>(pow(lhs, rhs)));
1074 1 return DirectiveError::NO_ERROR;
1075 }
1076 4 DirectiveError FpySequencer::op_flog() {
1077
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(F64)) {
1078 1 return DirectiveError::STACK_UNDERFLOW;
1079 }
1080 3 F64 val = this->m_runtime.stack.pop<F64>();
1081
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (val <= 0.0) {
1082 2 return DirectiveError::DOMAIN_ERROR;
1083 }
1084 1 this->m_runtime.stack.push(static_cast<F64>(log(val)));
1085 1 return DirectiveError::NO_ERROR;
1086 }
1087 8 DirectiveError FpySequencer::op_fmod() {
1088
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
1089 2 return DirectiveError::STACK_UNDERFLOW;
1090 }
1091 6 F64 rhs = this->m_runtime.stack.pop<F64>();
1092 6 F64 lhs = this->m_runtime.stack.pop<F64>();
1093 // std::fmod computes the exact truncated remainder (sign of lhs) with no
1094 // intermediate rounding. A zero divisor yields NaN, matching Rust and C#.
1095 6 F64 res = std::fmod(lhs, rhs);
1096 // Adjust to match Python's floored-modulo semantics: if the signs of the
1097 // remainder and divisor differ, add the divisor once. This mirrors op_smod
1098 // and is the exact frem + fadd the VM model computes (at most one rounded add).
1099
7/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
6 if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
1100 2 res += rhs;
1101
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 } else if (res == 0) {
1102 // Python normalizes an exact-multiple result so the zero carries the
1103 // divisor's sign (CPython float_rem); fmod leaves the dividend's.
1104 2 res = std::copysign(0.0, rhs);
1105 }
1106 6 this->m_runtime.stack.push(res);
1107 6 return DirectiveError::NO_ERROR;
1108 }
1109 6 DirectiveError FpySequencer::op_siext_8_64() {
1110
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
6 if (this->m_runtime.stack.size < sizeof(I8)) {
1111 1 return DirectiveError::STACK_UNDERFLOW;
1112 }
1113 // the result is wider than the operand, so it needs room to grow
1114
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (sizeof(I64) - sizeof(I8) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1115 1 return DirectiveError::STACK_OVERFLOW;
1116 }
1117 4 I8 src = this->m_runtime.stack.pop<I8>();
1118 4 this->m_runtime.stack.push(static_cast<I64>(src));
1119 4 return DirectiveError::NO_ERROR;
1120 }
1121 6 DirectiveError FpySequencer::op_siext_16_64() {
1122
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
6 if (this->m_runtime.stack.size < sizeof(I16)) {
1123 1 return DirectiveError::STACK_UNDERFLOW;
1124 }
1125 // the result is wider than the operand, so it needs room to grow
1126
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (sizeof(I64) - sizeof(I16) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1127 1 return DirectiveError::STACK_OVERFLOW;
1128 }
1129 4 I16 src = this->m_runtime.stack.pop<I16>();
1130 4 this->m_runtime.stack.push(static_cast<I64>(src));
1131 4 return DirectiveError::NO_ERROR;
1132 }
1133 6 DirectiveError FpySequencer::op_siext_32_64() {
1134
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
6 if (this->m_runtime.stack.size < sizeof(I32)) {
1135 1 return DirectiveError::STACK_UNDERFLOW;
1136 }
1137 // the result is wider than the operand, so it needs room to grow
1138
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (sizeof(I64) - sizeof(I32) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1139 1 return DirectiveError::STACK_OVERFLOW;
1140 }
1141 4 I32 src = this->m_runtime.stack.pop<I32>();
1142 4 this->m_runtime.stack.push(static_cast<I64>(src));
1143 4 return DirectiveError::NO_ERROR;
1144 }
1145 6 DirectiveError FpySequencer::op_ziext_8_64() {
1146
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
6 if (this->m_runtime.stack.size < sizeof(U8)) {
1147 1 return DirectiveError::STACK_UNDERFLOW;
1148 }
1149 // the result is wider than the operand, so it needs room to grow
1150
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (sizeof(U64) - sizeof(U8) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1151 1 return DirectiveError::STACK_OVERFLOW;
1152 }
1153 4 U8 src = this->m_runtime.stack.pop<U8>();
1154 4 this->m_runtime.stack.push(static_cast<U64>(src));
1155 4 return DirectiveError::NO_ERROR;
1156 }
1157 5 DirectiveError FpySequencer::op_ziext_16_64() {
1158
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (this->m_runtime.stack.size < sizeof(U16)) {
1159 1 return DirectiveError::STACK_UNDERFLOW;
1160 }
1161 // the result is wider than the operand, so it needs room to grow
1162
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (sizeof(U64) - sizeof(U16) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1163 1 return DirectiveError::STACK_OVERFLOW;
1164 }
1165 3 U16 src = this->m_runtime.stack.pop<U16>();
1166 3 this->m_runtime.stack.push(static_cast<U64>(src));
1167 3 return DirectiveError::NO_ERROR;
1168 }
1169 5 DirectiveError FpySequencer::op_ziext_32_64() {
1170
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (this->m_runtime.stack.size < sizeof(U32)) {
1171 1 return DirectiveError::STACK_UNDERFLOW;
1172 }
1173 // the result is wider than the operand, so it needs room to grow
1174
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (sizeof(U64) - sizeof(U32) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1175 1 return DirectiveError::STACK_OVERFLOW;
1176 }
1177 3 U32 src = this->m_runtime.stack.pop<U32>();
1178 3 this->m_runtime.stack.push(static_cast<U64>(src));
1179 3 return DirectiveError::NO_ERROR;
1180 }
1181 3 DirectiveError FpySequencer::op_itrunc_64_8() {
1182
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U64)) {
1183 1 return DirectiveError::STACK_UNDERFLOW;
1184 }
1185 2 U64 src = this->m_runtime.stack.pop<U64>();
1186 2 this->m_runtime.stack.push(static_cast<U8>(src));
1187 2 return DirectiveError::NO_ERROR;
1188 }
1189 3 DirectiveError FpySequencer::op_itrunc_64_16() {
1190
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U64)) {
1191 1 return DirectiveError::STACK_UNDERFLOW;
1192 }
1193 2 U64 src = this->m_runtime.stack.pop<U64>();
1194 2 this->m_runtime.stack.push(static_cast<U16>(src));
1195 2 return DirectiveError::NO_ERROR;
1196 }
1197 3 DirectiveError FpySequencer::op_itrunc_64_32() {
1198
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U64)) {
1199 1 return DirectiveError::STACK_UNDERFLOW;
1200 }
1201 2 U64 src = this->m_runtime.stack.pop<U64>();
1202 2 this->m_runtime.stack.push(static_cast<U32>(src));
1203 2 return DirectiveError::NO_ERROR;
1204 }
1205 10 DirectiveError FpySequencer::op_ffloor() {
1206
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 8 times.
10 if (this->m_runtime.stack.size < sizeof(F64)) {
1207 2 return DirectiveError::STACK_UNDERFLOW;
1208 }
1209 8 F64 val = this->m_runtime.stack.pop<F64>();
1210 // std::floor implements IEEE 754 roundToIntegralTowardNegative: +-0, +-inf
1211 // and NaN pass through, and the sign of a zero is preserved.
1212 8 this->m_runtime.stack.push(std::floor(val));
1213 8 return DirectiveError::NO_ERROR;
1214 }
1215 6 DirectiveError FpySequencer::op_iabs() {
1216
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(I64)) {
1217 2 return DirectiveError::STACK_UNDERFLOW;
1218 }
1219 4 I64 val = this->m_runtime.stack.pop<I64>();
1220 // abs(I64 min) is not representable in I64 (and -val on it is UB)
1221
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (val == std::numeric_limits<I64>::min()) {
1222 1 return DirectiveError::ARITHMETIC_OVERFLOW;
1223 }
1224 3 this->m_runtime.stack.push(val < 0 ? -val : val);
1225 3 return DirectiveError::NO_ERROR;
1226 }
1227 7 DirectiveError FpySequencer::op_fabs() {
1228
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(F64)) {
1229 2 return DirectiveError::STACK_UNDERFLOW;
1230 }
1231 5 F64 val = this->m_runtime.stack.pop<F64>();
1232 // IEEE 754 abs: clears the sign bit and changes nothing else, so NaN
1233 // payloads pass through.
1234 5 this->m_runtime.stack.push(std::fabs(val));
1235 5 return DirectiveError::NO_ERROR;
1236 }
1237 15 Signal FpySequencer::stackOp_directiveHandler(const FpySequencer_StackOpDirective& directive, DirectiveError& error) {
1238 // coding error, should not have gotten to this stack op handler
1239 15 FW_ASSERT((directive.get__op() >= Fpy::DirectiveId::OR && directive.get__op() <= Fpy::DirectiveId::ITRUNC_64_32) ||
1240 (directive.get__op() >= Fpy::DirectiveId::FFLOOR && directive.get__op() <= Fpy::DirectiveId::FABS),
1241 static_cast<FwAssertArgType>(directive.get__op()));
1242
1243
11/52
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 1 times.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
15 switch (directive.get__op()) {
1244 3 case Fpy::DirectiveId::OR:
1245
2/2
✓ Branch 2 taken 3 times.
✓ Branch 9 taken 3 times.
3 error = this->op_or();
1246 3 break;
1247 1 case Fpy::DirectiveId::AND:
1248
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_and();
1249 1 break;
1250 2 case Fpy::DirectiveId::IEQ:
1251
2/2
✓ Branch 2 taken 2 times.
✓ Branch 9 taken 2 times.
2 error = this->op_ieq();
1252 2 break;
1253 1 case Fpy::DirectiveId::INE:
1254
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_ine();
1255 1 break;
1256 1 case Fpy::DirectiveId::ULT:
1257
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_ult();
1258 1 break;
1259 ✗ case Fpy::DirectiveId::ULE:
1260 ✗ error = this->op_ule();
1261 ✗ break;
1262 1 case Fpy::DirectiveId::UGT:
1263
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_ugt();
1264 1 break;
1265 2 case Fpy::DirectiveId::UGE:
1266
2/2
✓ Branch 2 taken 2 times.
✓ Branch 9 taken 2 times.
2 error = this->op_uge();
1267 2 break;
1268 1 case Fpy::DirectiveId::SLT:
1269
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_slt();
1270 1 break;
1271 ✗ case Fpy::DirectiveId::SLE:
1272 ✗ error = this->op_sle();
1273 ✗ break;
1274 ✗ case Fpy::DirectiveId::SGT:
1275 ✗ error = this->op_sgt();
1276 ✗ break;
1277 ✗ case Fpy::DirectiveId::SGE:
1278 ✗ error = this->op_sge();
1279 ✗ break;
1280 ✗ case Fpy::DirectiveId::FEQ:
1281 ✗ error = this->op_feq();
1282 ✗ break;
1283 ✗ case Fpy::DirectiveId::FNE:
1284 ✗ error = this->op_fne();
1285 ✗ break;
1286 1 case Fpy::DirectiveId::FLT:
1287
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_flt();
1288 1 break;
1289 ✗ case Fpy::DirectiveId::FLE:
1290 ✗ error = this->op_fle();
1291 ✗ break;
1292 ✗ case Fpy::DirectiveId::FGT:
1293 ✗ error = this->op_fgt();
1294 ✗ break;
1295 1 case Fpy::DirectiveId::FGE:
1296
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_fge();
1297 1 break;
1298 1 case Fpy::DirectiveId::NOT:
1299
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_not();
1300 1 break;
1301 ✗ case Fpy::DirectiveId::FPEXT:
1302 ✗ error = this->op_fpext();
1303 ✗ break;
1304 ✗ case Fpy::DirectiveId::FPTRUNC:
1305 ✗ error = this->op_fptrunc();
1306 ✗ break;
1307 ✗ case Fpy::DirectiveId::FPTOSI:
1308 ✗ error = this->op_fptosi();
1309 ✗ break;
1310 ✗ case Fpy::DirectiveId::FPTOUI:
1311 ✗ error = this->op_fptoui();
1312 ✗ break;
1313 ✗ case Fpy::DirectiveId::SITOFP:
1314 ✗ error = this->op_sitofp();
1315 ✗ break;
1316 ✗ case Fpy::DirectiveId::UITOFP:
1317 ✗ error = this->op_uitofp();
1318 ✗ break;
1319 ✗ case Fpy::DirectiveId::ADD:
1320 ✗ error = this->op_add();
1321 ✗ break;
1322 ✗ case Fpy::DirectiveId::SUB:
1323 ✗ error = this->op_sub();
1324 ✗ break;
1325 ✗ case Fpy::DirectiveId::MUL:
1326 ✗ error = this->op_mul();
1327 ✗ break;
1328 ✗ case Fpy::DirectiveId::UDIV:
1329 ✗ error = this->op_udiv();
1330 ✗ break;
1331 ✗ case Fpy::DirectiveId::SDIV:
1332 ✗ error = this->op_sdiv();
1333 ✗ break;
1334 ✗ case Fpy::DirectiveId::UMOD:
1335 ✗ error = this->op_umod();
1336 ✗ break;
1337 ✗ case Fpy::DirectiveId::SMOD:
1338 ✗ error = this->op_smod();
1339 ✗ break;
1340 ✗ case Fpy::DirectiveId::FADD:
1341 ✗ error = this->op_fadd();
1342 ✗ break;
1343 ✗ case Fpy::DirectiveId::FSUB:
1344 ✗ error = this->op_fsub();
1345 ✗ break;
1346 ✗ case Fpy::DirectiveId::FMUL:
1347 ✗ error = this->op_fmul();
1348 ✗ break;
1349 ✗ case Fpy::DirectiveId::FDIV:
1350 ✗ error = this->op_fdiv();
1351 ✗ break;
1352 ✗ case Fpy::DirectiveId::FPOW:
1353 ✗ error = this->op_fpow();
1354 ✗ break;
1355 ✗ case Fpy::DirectiveId::FLOG:
1356 ✗ error = this->op_flog();
1357 ✗ break;
1358 ✗ case Fpy::DirectiveId::FMOD:
1359 ✗ error = this->op_fmod();
1360 ✗ break;
1361 ✗ case Fpy::DirectiveId::SIEXT_8_64:
1362 ✗ error = this->op_siext_8_64();
1363 ✗ break;
1364 ✗ case Fpy::DirectiveId::SIEXT_16_64:
1365 ✗ error = this->op_siext_16_64();
1366 ✗ break;
1367 ✗ case Fpy::DirectiveId::SIEXT_32_64:
1368 ✗ error = this->op_siext_32_64();
1369 ✗ break;
1370 ✗ case Fpy::DirectiveId::ZIEXT_8_64:
1371 ✗ error = this->op_ziext_8_64();
1372 ✗ break;
1373 ✗ case Fpy::DirectiveId::ZIEXT_16_64:
1374 ✗ error = this->op_ziext_16_64();
1375 ✗ break;
1376 ✗ case Fpy::DirectiveId::ZIEXT_32_64:
1377 ✗ error = this->op_ziext_32_64();
1378 ✗ break;
1379 ✗ case Fpy::DirectiveId::ITRUNC_64_8:
1380 ✗ error = this->op_itrunc_64_8();
1381 ✗ break;
1382 ✗ case Fpy::DirectiveId::ITRUNC_64_16:
1383 ✗ error = this->op_itrunc_64_16();
1384 ✗ break;
1385 ✗ case Fpy::DirectiveId::ITRUNC_64_32:
1386 ✗ error = this->op_itrunc_64_32();
1387 ✗ break;
1388 ✗ case Fpy::DirectiveId::FFLOOR:
1389 ✗ error = this->op_ffloor();
1390 ✗ break;
1391 ✗ case Fpy::DirectiveId::IABS:
1392 ✗ error = this->op_iabs();
1393 ✗ break;
1394 ✗ case Fpy::DirectiveId::FABS:
1395 ✗ error = this->op_fabs();
1396 ✗ break;
1397 ✗ default:
1398 ✗ FW_ASSERT(false, directive.get__op());
1399 ✗ break;
1400 }
1401
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 14 times.
15 if (error != DirectiveError::NO_ERROR) {
1402 1 return Signal::stmtResponse_failure;
1403 }
1404 14 return Signal::stmtResponse_success;
1405 }
1406
1407 10 Signal FpySequencer::exit_directiveHandler(const FpySequencer_ExitDirective& directive, DirectiveError& error) {
1408
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
10 if (this->m_runtime.stack.size < sizeof(I32)) {
1409 ✗ error = DirectiveError::STACK_UNDERFLOW;
1410 ✗ return Signal::stmtResponse_failure;
1411 }
1412 10 I32 errorCode = this->m_runtime.stack.pop<I32>();
1413 // exit(0), no error
1414
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (errorCode == 0) {
1415 // just goto the end of the sequence
1416 5 this->m_runtime.nextStatementIndex = this->m_sequenceObj.get_header().get_statementCount();
1417 5 return Signal::stmtResponse_success;
1418 }
1419 // otherwise, kill the sequence here
1420 // raise the user defined error code as an event
1421 5 this->log_WARNING_HI_SequenceExitedWithError(this->m_sequenceFilePath, errorCode);
1422 5 error = DirectiveError::EXIT_WITH_ERROR;
1423 5 return Signal::stmtResponse_failure;
1424 }
1425
1426 8 Signal FpySequencer::allocate_directiveHandler(const FpySequencer_AllocateDirective& directive, DirectiveError& error) {
1427
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 7 times.
8 if (directive.get_size() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1428 1 error = DirectiveError::STACK_OVERFLOW;
1429 1 return Signal::stmtResponse_failure;
1430 }
1431 7 this->m_runtime.stack.pushZeroes(directive.get_size());
1432 7 return Signal::stmtResponse_success;
1433 }
1434
1435 //! Helper to pop value from stack top and store at destOffset
1436 17 Signal FpySequencer::storeHelper(Fpy::StackSizeType destOffset, Fpy::StackSizeType size, DirectiveError& error) {
1437
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 15 times.
17 if (this->m_runtime.stack.size < size) {
1438 2 error = DirectiveError::STACK_UNDERFLOW;
1439 2 return Signal::stmtResponse_failure;
1440 }
1441 // After popping the value, would the write go out of bounds?
1442 15 Fpy::StackSizeType newStackSize = this->m_runtime.stack.size - size;
1443 // Overflow-safe check: destOffset + size > newStackSize
1444 // Rewritten as: check destOffset <= newStackSize first, then size > newStackSize - destOffset
1445
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
15 if (destOffset > newStackSize || size > newStackSize - destOffset) {
1446 4 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1447 4 return Signal::stmtResponse_failure;
1448 }
1449 // Copy value to the destination location
1450 11 this->m_runtime.stack.copy(destOffset, this->m_runtime.stack.size - size, size);
1451 11 this->m_runtime.stack.size = newStackSize;
1452 11 return Signal::stmtResponse_success;
1453 }
1454
1455 //! Helper to load value from srcOffset and push to stack top
1456 19 Signal FpySequencer::loadHelper(Fpy::StackSizeType srcOffset, Fpy::StackSizeType size, DirectiveError& error) {
1457
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 17 times.
19 if (size > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1458 2 error = DirectiveError::STACK_OVERFLOW;
1459 2 return Signal::stmtResponse_failure;
1460 }
1461 // Overflow-safe check: srcOffset + size > stack.size
1462 // Rewritten as: check srcOffset <= stack.size first, then size > stack.size - srcOffset
1463
4/4
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 2 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 14 times.
17 if (srcOffset > this->m_runtime.stack.size || size > this->m_runtime.stack.size - srcOffset) {
1464 3 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1465 3 return Signal::stmtResponse_failure;
1466 }
1467 // Copy from source location to top of stack
1468 14 this->m_runtime.stack.copy(this->m_runtime.stack.size, srcOffset, size);
1469 14 this->m_runtime.stack.size += size;
1470 14 return Signal::stmtResponse_success;
1471 }
1472
1473 7 Signal FpySequencer::storeRelConstOffset_directiveHandler(const FpySequencer_StoreRelConstOffsetDirective& directive,
1474 DirectiveError& error) {
1475 7 I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1476
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
7 if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1477 2 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1478 2 return Signal::stmtResponse_failure;
1479 }
1480 5 return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1481 }
1482
1483 16 Signal FpySequencer::loadRel_directiveHandler(const FpySequencer_LoadRelDirective& directive, DirectiveError& error) {
1484 16 I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1485
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14 times.
16 if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1486 2 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1487 2 return Signal::stmtResponse_failure;
1488 }
1489 14 return this->loadHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1490 }
1491
1492 53 Signal FpySequencer::pushVal_directiveHandler(const FpySequencer_PushValDirective& directive, DirectiveError& error) {
1493
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 52 times.
53 if (directive.get__valSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1494 1 error = DirectiveError::STACK_OVERFLOW;
1495 1 return Signal::stmtResponse_failure;
1496 }
1497 // copy from the bytearray in the directive to the stack, add to stack size.
1498 104 this->m_runtime.stack.push(const_cast<U8*>(directive.get_val()),
1499 52 static_cast<Fpy::StackSizeType>(directive.get__valSize()));
1500 52 return Signal::stmtResponse_success;
1501 }
1502
1503 20 Signal FpySequencer::discard_directiveHandler(const FpySequencer_DiscardDirective& directive, DirectiveError& error) {
1504
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 19 times.
20 if (this->m_runtime.stack.size < directive.get_size()) {
1505 1 error = DirectiveError::STACK_UNDERFLOW;
1506 1 return Signal::stmtResponse_failure;
1507 }
1508 // drop the specified amount of bytes off the stack. simple as.
1509 19 this->m_runtime.stack.size -= directive.get_size();
1510 19 return Signal::stmtResponse_success;
1511 }
1512
1513 5 Signal FpySequencer::memCmp_directiveHandler(const FpySequencer_MemCmpDirective& directive, DirectiveError& error) {
1514 // Overflow-safe check: we need size * 2 bytes on the stack
1515 // First check that size * 2 doesn't overflow: size > MAX/2 would overflow
1516 // MAX_STACK_SIZE is the upper bound for stack.size, so if size > MAX_STACK_SIZE/2, we definitely don't have enough
1517
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (directive.get_size() > Fpy::MAX_STACK_SIZE / 2) {
1518 1 error = DirectiveError::STACK_UNDERFLOW;
1519 1 return Signal::stmtResponse_failure;
1520 }
1521 // Now safe to compute size * 2
1522
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 3 times.
4 if (this->m_runtime.stack.size < directive.get_size() * 2) {
1523 1 error = DirectiveError::STACK_UNDERFLOW;
1524 1 return Signal::stmtResponse_failure;
1525 }
1526
1527 // find the starting offsets of the two byte arrays
1528 3 U64 lhsOffset = this->m_runtime.stack.size - directive.get_size() * 2;
1529 3 U64 rhsOffset = this->m_runtime.stack.size - directive.get_size();
1530
1531 // "officially" remove them from the stack
1532 // you have to do this before pushing to the stack, otherwise the result would get placed
1533 // after the byte arrays
1534 3 this->m_runtime.stack.size -= directive.get_size() * 2;
1535
1536 // memcmp the two byte arrays, push FW_SERIALIZE_TRUE_VALUE if they were equal, FW_SERIALIZE_FALSE_VALUE otherwise
1537
2/4
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 if (memcmp(this->m_runtime.stack.bytes + lhsOffset, this->m_runtime.stack.bytes + rhsOffset,
1538
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
6 directive.get_size()) == 0) {
1539 2 this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_TRUE_VALUE));
1540 } else {
1541 1 this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_FALSE_VALUE));
1542 }
1543 3 return Signal::stmtResponse_success;
1544 }
1545
1546 5 Signal FpySequencer::stackCmd_directiveHandler(const FpySequencer_StackCmdDirective& directive, DirectiveError& error) {
1547 // Overflow-safe check: need argsSize + sizeof(FwOpcodeType) bytes
1548 // Check stack.size >= sizeof(FwOpcodeType) first, then stack.size - sizeof(FwOpcodeType) >= argsSize
1549
4/4
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3 times.
8 if (this->m_runtime.stack.size < sizeof(FwOpcodeType) ||
1550
1/2
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 this->m_runtime.stack.size - sizeof(FwOpcodeType) < directive.get_argsSize()) {
1551 2 error = DirectiveError::STACK_UNDERFLOW;
1552 2 return Signal::stmtResponse_failure;
1553 }
1554
1555 // pop the opcode of the cmd off the stack
1556 // note this means that, unlike the actual byte array that the dispatcher gets,
1557 // these cmds have opcode after the argument buffer
1558 3 FwOpcodeType opcode = this->m_runtime.stack.pop<FwOpcodeType>();
1559 3 U64 argBufOffset = this->m_runtime.stack.size - directive.get_argsSize();
1560
1561 // update the opcode of the cmd we will await
1562 3 this->m_runtime.currentCmdOpcode = opcode;
1563
1564 // also pop the args off the stack
1565 3 this->m_runtime.stack.size -= directive.get_argsSize();
1566
1567 // the cmd response code will be pushed to the stack when it comes back, so make sure
1568 // there is room for it now, before the cmd is dispatched. popping the opcode above
1569 // frees some room, but FwOpcodeType is configurable so it may not be enough
1570
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (sizeof(Fw::CmdResponse::SerialType) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1571 ✗ error = DirectiveError::STACK_OVERFLOW;
1572 ✗ return Signal::stmtResponse_failure;
1573 }
1574
1575
2/3
✓ Branch 9 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
3 if (this->sendCmd(opcode, this->m_runtime.stack.bytes + argBufOffset, directive.get_argsSize()) ==
1576 Fw::Success::FAILURE) {
1577 ✗ return Signal::stmtResponse_failure;
1578 } else {
1579 // now tell the SM to wait some more until we get the cmd response back
1580 // if we've already got the response back this should be harmless
1581 3 return Signal::stmtResponse_keepWaiting;
1582 }
1583
1584 return Signal::stmtResponse_success;
1585 }
1586
1587 4 Signal FpySequencer::pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& error) {
1588
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (Fw::Time::SERIALIZED_SIZE > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1589
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
1590 1 return Signal::stmtResponse_failure;
1591 }
1592
1593
1/1
✓ Branch 3 taken 3 times.
3 Fw::Time currentTime = this->getTime();
1594
1595 3 U8 currentTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
1596
1/1
✓ Branch 2 taken 3 times.
3 Fw::ExternalSerializeBuffer timeEsb(currentTimeBuf, Fw::Time::SERIALIZED_SIZE);
1597
1/1
✓ Branch 2 taken 3 times.
3 Fw::SerializeStatus stat = timeEsb.serializeFrom(currentTime);
1598
1599 // coding error if this failed, we should have enough space
1600 3 FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1601
1602 // push time to end of stack
1603
2/2
✓ Branch 5 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
1604 3 return Signal::stmtResponse_success;
1605 3 }
1606
1607 3 Signal FpySequencer::setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& error) {
1608
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U32)) {
1609 1 error = DirectiveError::STACK_UNDERFLOW;
1610 1 return Signal::stmtResponse_failure;
1611 }
1612
1613 2 U32 seed = this->m_runtime.stack.pop<U32>();
1614 2 this->m_runtime.rng.seed(seed);
1615 2 this->m_runtime.rngSeeded = true;
1616 2 return Signal::stmtResponse_success;
1617 }
1618
1619 7 Signal FpySequencer::pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& error) {
1620
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (sizeof(U32) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1621 1 error = DirectiveError::STACK_OVERFLOW;
1622 1 return Signal::stmtResponse_failure;
1623 }
1624
1625
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
6 if (!this->m_runtime.rngSeeded) {
1626
1/1
✓ Branch 3 taken 2 times.
2 Fw::Time currentTime = this->getTime();
1627
2/2
✓ Branch 3 taken 2 times.
✓ Branch 10 taken 2 times.
8 std::seed_seq seedSeq{static_cast<U32>(currentTime.getTimeBase()), static_cast<U32>(currentTime.getContext()),
1628
3/3
✓ Branch 4 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 11 taken 2 times.
8 currentTime.getSeconds(), currentTime.getUSeconds()};
1629
1/1
✓ Branch 4 taken 2 times.
2 this->m_runtime.rng.seed(seedSeq);
1630 2 this->m_runtime.rngSeeded = true;
1631 2 }
1632
1633 6 U32 randVal = static_cast<U32>(this->m_runtime.rng());
1634 6 this->m_runtime.stack.push(randVal);
1635 6 return Signal::stmtResponse_success;
1636 }
1637
1638 8 Signal FpySequencer::getField_directiveHandler(const FpySequencer_GetFieldDirective& directive, DirectiveError& error) {
1639 // Need sizeof(StackSizeType) for the offset AND parentSize for the parent data
1640 // Check we have enough for the offset first
1641
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 7 times.
8 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1642 1 error = DirectiveError::STACK_UNDERFLOW;
1643 1 return Signal::stmtResponse_failure;
1644 }
1645 // After popping the offset, we need at least parentSize bytes remaining
1646
2/2
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 5 times.
7 if (this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < directive.get_parentSize()) {
1647 2 error = DirectiveError::STACK_UNDERFLOW;
1648 2 return Signal::stmtResponse_failure;
1649 }
1650
1651 5 Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1652
1653 // Overflow-safe check: offset + memberSize > parentSize
1654 // Rewritten as: check offset <= parentSize first, then memberSize > parentSize - offset
1655
6/6
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
✓ Branch 14 taken 2 times.
✓ Branch 15 taken 2 times.
✓ Branch 16 taken 3 times.
✓ Branch 17 taken 2 times.
5 if (offset > directive.get_parentSize() || directive.get_memberSize() > directive.get_parentSize() - offset) {
1656 // i think it's somewhat ambiguous whether this is a stack access out of bounds
1657 // but there isn't really an error code that better reflects this, and i guess
1658 // it's technically true
1659 3 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1660 3 return Signal::stmtResponse_failure;
1661 }
1662
1663 // the resulting bytes should move to the start of the parent array
1664
1665 // Calculate the offset of the parent start in the stack
1666 2 Fpy::StackSizeType parentStartOffset = this->m_runtime.stack.size - directive.get_parentSize();
1667 // Overflow-safe: parentStartOffset + offset cannot overflow since offset <= parentSize
1668 // and parentStartOffset + parentSize == stack.size (which is bounded)
1669 2 this->m_runtime.stack.move(parentStartOffset, parentStartOffset + offset, directive.get_memberSize());
1670 // adjust stack size by the diff between the member and the parent
1671 2 this->m_runtime.stack.size -= (directive.get_parentSize() - directive.get_memberSize());
1672 2 return Signal::stmtResponse_success;
1673 }
1674
1675 9 Signal FpySequencer::peek_directiveHandler(const FpySequencer_PeekDirective& directive, DirectiveError& error) {
1676 // must have at least two StackSizeType on stack
1677
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 7 times.
9 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) * 2) {
1678 2 error = DirectiveError::STACK_UNDERFLOW;
1679 2 return Signal::stmtResponse_failure;
1680 }
1681
1682 7 Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1683 7 Fpy::StackSizeType byteCount = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1684
1685 // Check offset doesn't exceed stack size (after both pops)
1686
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (offset > this->m_runtime.stack.size) {
1687 // would access past the bottom of the stack
1688 // note we allow the equals case because the byteCount might be 0
1689 1 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1690 1 return Signal::stmtResponse_failure;
1691 }
1692
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (byteCount > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1693 // we would overflow the stack if we pushed this many bytes to it
1694 2 error = DirectiveError::STACK_OVERFLOW;
1695 2 return Signal::stmtResponse_failure;
1696 }
1697 // Overflow-safe check: byteCount + offset > stack.size
1698 // Rewritten as: check offset <= stack.size (done above), then byteCount > stack.size - offset
1699
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (byteCount > this->m_runtime.stack.size - offset) {
1700 // would access past the bottom of the stack
1701 ✗ error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1702 ✗ return Signal::stmtResponse_failure;
1703 }
1704 // start copying from the lowest byte of the src array
1705 4 U8* src = this->m_runtime.stack.top() - offset - byteCount;
1706 4 this->m_runtime.stack.push(src, byteCount);
1707 4 return Signal::stmtResponse_success;
1708 }
1709
1710 7 Signal FpySequencer::storeRel_directiveHandler(const FpySequencer_StoreRelDirective& directive, DirectiveError& error) {
1711 // Need enough bytes for the value and the offset (SignedStackSizeType = 4 bytes)
1712 // Overflow-safe: check stack.size >= sizeof(SignedStackSizeType) first, then stack.size -
1713 // sizeof(SignedStackSizeType) >= size
1714
4/4
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 5 times.
13 if (this->m_runtime.stack.size < sizeof(Fpy::SignedStackSizeType) ||
1715
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 5 times.
6 this->m_runtime.stack.size - sizeof(Fpy::SignedStackSizeType) < directive.get_size()) {
1716 2 error = DirectiveError::STACK_UNDERFLOW;
1717 2 return Signal::stmtResponse_failure;
1718 }
1719
1720 // Pop the signed offset from the stack
1721 5 Fpy::SignedStackSizeType lvarOffset = this->m_runtime.stack.pop<Fpy::SignedStackSizeType>();
1722
1723 5 I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + lvarOffset;
1724
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
5 if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1725 1 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1726 1 return Signal::stmtResponse_failure;
1727 }
1728 4 return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1729 }
1730
1731 5 Signal FpySequencer::call_directiveHandler(const FpySequencer_CallDirective& directive, DirectiveError& error) {
1732 // Need at least 4 bytes for the target address
1733
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (this->m_runtime.stack.size < sizeof(U32)) {
1734 1 error = DirectiveError::STACK_UNDERFLOW;
1735 1 return Signal::stmtResponse_failure;
1736 }
1737
1738 // Pop the target directive index from the stack
1739 4 U32 target = this->m_runtime.stack.pop<U32>();
1740
1741 // Check if we have space to push return address and saved frame pointer (8 bytes total)
1742
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size + sizeof(Fpy::StackSizeType) + sizeof(U32) > Fpy::MAX_STACK_SIZE) {
1743 1 error = DirectiveError::STACK_OVERFLOW;
1744 1 return Signal::stmtResponse_failure;
1745 }
1746
1747 // Check target is within bounds (will also be checked at execution time)
1748
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 2 times.
3 if (target > m_sequenceObj.get_header().get_statementCount()) {
1749 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
1750 1 return Signal::stmtResponse_failure;
1751 }
1752
1753 // Save the return address (next instruction after CALL)
1754 2 U32 returnAddr = this->m_runtime.nextStatementIndex;
1755
1756 // Set the next instruction to the target
1757 2 this->m_runtime.nextStatementIndex = target;
1758
1759 // Push the return address to the stack
1760 2 this->m_runtime.stack.push<U32>(returnAddr);
1761
1762 // Push the current frame pointer to the stack
1763 2 this->m_runtime.stack.push<Fpy::StackSizeType>(this->m_runtime.stack.currentFrameStart);
1764
1765 // Set the new frame pointer to the current top of stack
1766 2 this->m_runtime.stack.currentFrameStart = this->m_runtime.stack.size;
1767
1768 2 return Signal::stmtResponse_success;
1769 }
1770
1771 7 Signal FpySequencer::return_directiveHandler(const FpySequencer_ReturnDirective& directive, DirectiveError& error) {
1772 7 Fpy::StackSizeType returnValSize = directive.get_returnValSize();
1773 7 Fpy::StackSizeType callArgsSize = directive.get_callArgsSize();
1774
1775 // Check we have enough bytes for the return value
1776
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (this->m_runtime.stack.size < returnValSize) {
1777 1 error = DirectiveError::STACK_UNDERFLOW;
1778 1 return Signal::stmtResponse_failure;
1779 }
1780
1781 // Remember where the return value lives; it is moved down the stack below rather than copied
1782 // through a local buffer, which at Fpy::MAX_STACK_SIZE would not fit a typical task stack
1783 6 const Fpy::StackSizeType returnValOffset = this->m_runtime.stack.size - returnValSize;
1784
1785 // Truncate the stack to stack_frame_start (discard all local variables)
1786
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 5 times.
6 if (this->m_runtime.stack.currentFrameStart > this->m_runtime.stack.size) {
1787 1 error = DirectiveError::FRAME_START_OUT_OF_BOUNDS;
1788 1 return Signal::stmtResponse_failure;
1789 }
1790 5 this->m_runtime.stack.size = this->m_runtime.stack.currentFrameStart;
1791
1792 // Check we have enough bytes for saved frame pointer and return address
1793
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) + sizeof(U32)) {
1794 ✗ error = DirectiveError::STACK_UNDERFLOW;
1795 ✗ return Signal::stmtResponse_failure;
1796 }
1797
1798 // Pop the saved frame pointer
1799 5 Fpy::StackSizeType savedFramePtr = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1800
1801 // Pop the return address
1802 5 U32 returnAddr = this->m_runtime.stack.pop<U32>();
1803
1804 // Restore the frame pointer
1805
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (savedFramePtr > this->m_runtime.stack.size) {
1806 1 error = DirectiveError::FRAME_START_OUT_OF_BOUNDS;
1807 1 return Signal::stmtResponse_failure;
1808 }
1809 4 this->m_runtime.stack.currentFrameStart = savedFramePtr;
1810
1811 // Validate the return address is within bounds
1812
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 3 times.
4 if (returnAddr > m_sequenceObj.get_header().get_statementCount()) {
1813 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
1814 1 return Signal::stmtResponse_failure;
1815 }
1816
1817 // Set the next instruction to the return address
1818 3 this->m_runtime.nextStatementIndex = returnAddr;
1819
1820 // Check that we have enough bytes for the call arguments
1821
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < callArgsSize) {
1822 1 error = DirectiveError::STACK_UNDERFLOW;
1823 1 return Signal::stmtResponse_failure;
1824 }
1825 // Discard the function arguments
1826 2 this->m_runtime.stack.size -= callArgsSize;
1827
1828 // Push the return value
1829
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 if (returnValSize > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1830 ✗ error = DirectiveError::STACK_OVERFLOW;
1831 ✗ return Signal::stmtResponse_failure;
1832 }
1833
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (returnValSize > 0) {
1834 // Not Stack::move: the source region sits above the truncated stack size, which
1835 // Stack::move rejects. Both regions were bounds-checked above against MAX_STACK_SIZE.
1836
2/4
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
1 (void)memmove(this->m_runtime.stack.top(), &this->m_runtime.stack.bytes[returnValOffset], returnValSize);
1837 1 this->m_runtime.stack.size += returnValSize;
1838 }
1839
1840 2 return Signal::stmtResponse_success;
1841 }
1842
1843 5 Signal FpySequencer::loadAbs_directiveHandler(const FpySequencer_LoadAbsDirective& directive, DirectiveError& error) {
1844 5 return this->loadHelper(directive.get_globalOffset(), directive.get_size(), error);
1845 }
1846
1847 5 Signal FpySequencer::storeAbs_directiveHandler(const FpySequencer_StoreAbsDirective& directive, DirectiveError& error) {
1848 5 Fpy::StackSizeType size = directive.get_size();
1849
1850 // Need enough bytes for the value and the offset
1851 // Overflow-safe: check stack.size >= sizeof(StackSizeType) first, then stack.size - sizeof >= size
1852
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
5 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) ||
1853
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < size) {
1854 2 error = DirectiveError::STACK_UNDERFLOW;
1855 2 return Signal::stmtResponse_failure;
1856 }
1857
1858 // Pop the global offset from the stack
1859 3 Fpy::StackSizeType globalOffset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1860
1861 3 return this->storeHelper(globalOffset, size, error);
1862 }
1863
1864 5 Signal FpySequencer::storeAbsConstOffset_directiveHandler(const FpySequencer_StoreAbsConstOffsetDirective& directive,
1865 DirectiveError& error) {
1866 5 return this->storeHelper(directive.get_globalOffset(), directive.get_size(), error);
1867 }
1868
1869 12 Signal FpySequencer::popEvent_directiveHandler(const FpySequencer_PopEventDirective& directive, DirectiveError& error) {
1870 // Pop messageSize from the stack
1871
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 11 times.
12 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1872
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_UNDERFLOW;
1873 1 return Signal::stmtResponse_failure;
1874 }
1875
1/1
✓ Branch 5 taken 11 times.
11 Fpy::StackSizeType messageSize = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1876
1877 11 const Fpy::StackSizeType severitySize = static_cast<Fpy::StackSizeType>(sizeof(Fw::LogSeverity::SerialType));
1878
1879 // Need message_size bytes + sizeof(LogSeverity serial type) for severity
1880
3/4
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 9 times.
11 if (this->m_runtime.stack.size < severitySize || this->m_runtime.stack.size - severitySize < messageSize) {
1881
1/1
✓ Branch 4 taken 2 times.
2 error = DirectiveError::STACK_UNDERFLOW;
1882 2 return Signal::stmtResponse_failure;
1883 }
1884
1885 // Pop message bytes first
1886 9 U8 messageBuf[FW_LOG_STRING_MAX_SIZE] = {};
1887 // don't read in more than (log string size) - 1 bytes
1888 9 Fpy::StackSizeType clampedSize = std::min(messageSize, static_cast<Fpy::StackSizeType>(FW_LOG_STRING_MAX_SIZE - 1));
1889 // If message is larger than buffer, discard the excess bytes first (from top of stack, which is the end of the
1890 // message)
1891
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (messageSize > clampedSize) {
1892 ✗ Fpy::StackSizeType excess = messageSize - clampedSize;
1893 ✗ FW_ASSERT(this->m_runtime.stack.size >= excess, static_cast<FwAssertArgType>(this->m_runtime.stack.size),
1894 static_cast<FwAssertArgType>(excess));
1895 ✗ this->m_runtime.stack.size -= excess;
1896 }
1897
1/1
✓ Branch 4 taken 9 times.
9 this->m_runtime.stack.pop(messageBuf, clampedSize);
1898 9 messageBuf[clampedSize] = '\0';
1899
1900 // Pop severity
1901
1/1
✓ Branch 4 taken 9 times.
9 Fw::LogSeverity::SerialType severity = this->m_runtime.stack.pop<Fw::LogSeverity::SerialType>();
1902
1903 // Construct the message string
1904
1/1
✓ Branch 2 taken 9 times.
9 Fw::String messageStr(reinterpret_cast<const char*>(messageBuf));
1905
1906 // Emit the appropriate event based on severity
1907
8/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
9 switch (severity) {
1908 1 case Fw::LogSeverity::FATAL:
1909
1/1
✓ Branch 8 taken 1 times.
1 this->log_FATAL_LogFatal(this->m_sequenceFilePath, messageStr);
1910 1 break;
1911 1 case Fw::LogSeverity::WARNING_HI:
1912
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_LogWarningHi(this->m_sequenceFilePath, messageStr);
1913 1 break;
1914 1 case Fw::LogSeverity::WARNING_LO:
1915
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_LO_LogWarningLo(this->m_sequenceFilePath, messageStr);
1916 1 break;
1917 1 case Fw::LogSeverity::COMMAND:
1918
1/1
✓ Branch 8 taken 1 times.
1 this->log_COMMAND_LogCommand(this->m_sequenceFilePath, messageStr);
1919 1 break;
1920 2 case Fw::LogSeverity::ACTIVITY_HI:
1921
1/1
✓ Branch 8 taken 2 times.
2 this->log_ACTIVITY_HI_LogActivityHi(this->m_sequenceFilePath, messageStr);
1922 2 break;
1923 1 case Fw::LogSeverity::ACTIVITY_LO:
1924
1/1
✓ Branch 8 taken 1 times.
1 this->log_ACTIVITY_LO_LogActivityLo(this->m_sequenceFilePath, messageStr);
1925 1 break;
1926 1 case Fw::LogSeverity::DIAGNOSTIC:
1927
1/1
✓ Branch 8 taken 1 times.
1 this->log_DIAGNOSTIC_LogDiagnostic(this->m_sequenceFilePath, messageStr);
1928 1 break;
1929 1 default:
1930
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::INVALID_ARG;
1931 1 return Signal::stmtResponse_failure;
1932 }
1933
1934 8 return Signal::stmtResponse_success;
1935 9 }
1936
1937 10 Signal FpySequencer::popSerializable_directiveHandler(const FpySequencer_PopSerializableDirective& directive,
1938 DirectiveError& error) {
1939 // No size assertion here: an oversized size is untrusted sequence content and is rejected by
1940 // the stack check below, since the stack can never hold more than Fpy::MAX_STACK_SIZE bytes
1941
1942 // Validate port index is in range (using enum constant value)
1943 10 constexpr FwIndexType MAX_PORTS = static_cast<FwIndexType>(Svc::Fpy::SerialPortIndex::MAX_SERIAL_PORTS);
1944 10 const FwIndexType portIndex = directive.get_portIndex();
1945
1946 // Check for negative port index or out of bounds
1947
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 9 times.
10 if (portIndex < 0 || portIndex >= MAX_PORTS) {
1948
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::SERIAL_PORT_INVALID_INDEX;
1949 1 return Signal::stmtResponse_failure;
1950 }
1951
1952 // Check port is connected
1953
3/3
✓ Branch 5 taken 9 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 8 times.
9 if (!this->isConnected_serialOut_OutputPort(portIndex)) {
1954
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::SERIAL_PORT_NOT_CONNECTED;
1955 1 return Signal::stmtResponse_failure;
1956 }
1957
1958 // Validate data size on stack
1959
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 7 times.
8 if (this->m_runtime.stack.size < directive.get_size()) {
1960
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_UNDERFLOW;
1961 1 return Signal::stmtResponse_failure;
1962 }
1963
1964 // Create external buffer referencing stack data (no copy)
1965
1/1
✓ Branch 4 taken 7 times.
7 U8* dataPtr = this->m_runtime.stack.top() - directive.get_size();
1966
1/1
✓ Branch 6 taken 7 times.
7 Fw::ExternalSerializeBuffer buf(dataPtr, directive.get_size());
1967
1968 // Set buffer length and verify success
1969
1/1
✓ Branch 6 taken 7 times.
7 Fw::SerializeStatus stat = buf.setBuffLen(directive.get_size());
1970 7 FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1971
1972 // Call output port; a typed downstream port reports deserialize failures here, which is
1973 // untrusted sequence content (e.g. undersized payload), not an invariant to assert on
1974
1/1
✓ Branch 5 taken 7 times.
7 Fw::SerializeStatus portStatus = this->serialOut_out(portIndex, buf);
1975
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (portStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
1976
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::SERIAL_PORT_DESERIALIZE_FAILURE;
1977 1 return Signal::stmtResponse_failure;
1978 }
1979
1980 // Pop data from stack
1981 6 this->m_runtime.stack.size -= directive.get_size();
1982
1983 6 return Signal::stmtResponse_success;
1984 7 }
1985
1986 } // namespace Svc
1987