Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Svc/FpySequencer/FpySequencer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ FpySequencer ::FpySequencer(const char* const compName)
m_goalState(),
m_sequencesStarted(0),
m_statementsDispatched(0),
m_rng(),
m_rngSeeded(false),
m_runtime(),
m_breakpoint(),
m_tlm() {}
Expand Down
15 changes: 15 additions & 0 deletions Svc/FpySequencer/FpySequencer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "Svc/FpySequencer/SequenceSerializableAc.hpp"
#include "Svc/FpySequencer/StatementSerializableAc.hpp"
#include "config/FppConstantsAc.hpp"
#include <random>

static_assert(Svc::Fpy::MAX_SEQUENCE_ARG_COUNT <= std::numeric_limits<U8>::max(),
"Sequence arg count must be below U8 max");
Expand Down Expand Up @@ -65,9 +66,11 @@
FpySequencer_MemCmpDirective memCmp;
FpySequencer_StackCmdDirective stackCmd;
FpySequencer_PushTimeDirective pushTime;
FpySequencer_SetSeedDirective setSeed;
FpySequencer_PushRandDirective pushRand;
FpySequencer_SetFlagDirective setFlag;
FpySequencer_GetFlagDirective getFlag;
FpySequencer_GetFieldDirective getField;

Check failure on line 73 in Svc/FpySequencer/FpySequencer.hpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)

Check failure on line 73 in Svc/FpySequencer/FpySequencer.hpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)
FpySequencer_PeekDirective peek;
FpySequencer_StoreRelDirective storeRel;
FpySequencer_CallDirective call;
Expand Down Expand Up @@ -552,9 +555,15 @@
//! Internal interface handler for directive_pushTime
void directive_pushTime_internalInterfaceHandler(const Svc::FpySequencer_PushTimeDirective& directive) override;

//! Internal interface handler for directive_setSeed
void directive_setSeed_internalInterfaceHandler(const Svc::FpySequencer_SetSeedDirective& directive) override;

//! Internal interface handler for directive_pushRand
void directive_pushRand_internalInterfaceHandler(const Svc::FpySequencer_PushRandDirective& directive) override;

//! Internal interface handler for directive_setFlag
void directive_setFlag_internalInterfaceHandler(const Svc::FpySequencer_SetFlagDirective& directive) override;

Check failure on line 566 in Svc/FpySequencer/FpySequencer.hpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)

Check failure on line 566 in Svc/FpySequencer/FpySequencer.hpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)
//! Internal interface handler for directive_getFlag
void directive_getFlag_internalInterfaceHandler(const Svc::FpySequencer_GetFlagDirective& directive) override;

Expand Down Expand Up @@ -625,6 +634,10 @@
// otherwise, since construction
U64 m_statementsDispatched;

// per-component RNG state used by PUSH_RAND and later SET_SEED-style directives
std::mt19937 m_rng;
bool m_rngSeeded = false;

// the runtime state of the sequence. encapsulates all state
// needed to run the sequence.
// this is distinct from the state of the sequencer. the
Expand Down Expand Up @@ -857,6 +870,8 @@
Signal memCmp_directiveHandler(const FpySequencer_MemCmpDirective& directive, DirectiveError& error);
Signal stackCmd_directiveHandler(const FpySequencer_StackCmdDirective& directive, DirectiveError& error);
Signal pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& error);
Signal setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& error);
Signal pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& error);

Check failure on line 874 in Svc/FpySequencer/FpySequencer.hpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)

Check failure on line 874 in Svc/FpySequencer/FpySequencer.hpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)
Signal setFlag_directiveHandler(const FpySequencer_SetFlagDirective& directive, DirectiveError& error);
Signal getFlag_directiveHandler(const FpySequencer_GetFlagDirective& directive, DirectiveError& error);
Signal getField_directiveHandler(const FpySequencer_GetFieldDirective& directive, DirectiveError& error);
Expand Down
47 changes: 47 additions & 0 deletions Svc/FpySequencer/FpySequencerDirectives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,20 @@
handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TIME, error);
}

//! Internal interface handler for directive_setSeed
void FpySequencer::directive_setSeed_internalInterfaceHandler(const Svc::FpySequencer_SetSeedDirective& directive) {
DirectiveError error = DirectiveError::NO_ERROR;
this->sendSignal(this->setSeed_directiveHandler(directive, error));
handleDirectiveErrorCode(Fpy::DirectiveId::SET_SEED, error);
}

//! Internal interface handler for directive_pushRand
void FpySequencer::directive_pushRand_internalInterfaceHandler(const Svc::FpySequencer_PushRandDirective& directive) {
DirectiveError error = DirectiveError::NO_ERROR;
this->sendSignal(this->pushRand_directiveHandler(directive, error));
handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_RAND, error);
}

//! Internal interface handler for directive_setFlag
void FpySequencer::directive_setFlag_internalInterfaceHandler(const Svc::FpySequencer_SetFlagDirective& directive) {
DirectiveError error = DirectiveError::NO_ERROR;
Expand All @@ -225,7 +239,7 @@
//! Internal interface handler for directive_getField
void FpySequencer::directive_getField_internalInterfaceHandler(const Svc::FpySequencer_GetFieldDirective& directive) {
DirectiveError error = DirectiveError::NO_ERROR;
this->sendSignal(this->getField_directiveHandler(directive, error));

Check failure on line 242 in Svc/FpySequencer/FpySequencerDirectives.cpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)

Check failure on line 242 in Svc/FpySequencer/FpySequencerDirectives.cpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)
handleDirectiveErrorCode(Fpy::DirectiveId::GET_FIELD, error);
}

Expand Down Expand Up @@ -1296,6 +1310,39 @@
return Signal::stmtResponse_success;
}

Signal FpySequencer::setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& error) {
if (this->m_runtime.stack.size < sizeof(U32)) {
error = DirectiveError::STACK_UNDERFLOW;
return Signal::stmtResponse_failure;
}

U32 seed = this->m_runtime.stack.pop<U32>();
this->m_rng.seed(seed);
this->m_rngSeeded = true;
return Signal::stmtResponse_success;
}

Signal FpySequencer::pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& error) {
if (Fpy::MAX_STACK_SIZE - sizeof(U32) < this->m_runtime.stack.size) {
error = DirectiveError::STACK_OVERFLOW;
return Signal::stmtResponse_failure;
}

if (!this->m_rngSeeded) {
Fw::Time currentTime = this->getTime();
std::seed_seq seedSeq{static_cast<U32>(currentTime.getTimeBase()),
static_cast<U32>(currentTime.getContext()),
currentTime.getSeconds(),
currentTime.getUSeconds()};
this->m_rng.seed(seedSeq);
this->m_rngSeeded = true;
}

U32 randVal = static_cast<U32>(this->m_rng());
this->m_runtime.stack.push(randVal);
return Signal::stmtResponse_success;
}

Signal FpySequencer::setFlag_directiveHandler(const FpySequencer_SetFlagDirective& directive, DirectiveError& error) {
if (this->m_runtime.stack.size < 1) {
error = DirectiveError::STACK_UNDERFLOW;
Expand Down Expand Up @@ -1335,7 +1382,7 @@
if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
error = DirectiveError::STACK_UNDERFLOW;
return Signal::stmtResponse_failure;
}

Check failure on line 1385 in Svc/FpySequencer/FpySequencerDirectives.cpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)

Check failure on line 1385 in Svc/FpySequencer/FpySequencerDirectives.cpp

View workflow job for this annotation

GitHub Actions / Check Spelling

`aaf` is not a recognized word (unrecognized-spelling)
// After popping the offset, we need at least parentSize bytes remaining
if (this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < directive.get_parentSize()) {
error = DirectiveError::STACK_UNDERFLOW;
Expand Down
14 changes: 14 additions & 0 deletions Svc/FpySequencer/FpySequencerDirectives.fppi
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ struct PushTimeDirective {
_empty: U8
}

@ pops a U32 from the stack and uses it to seed the RNG used by PushRandDirective
struct SetSeedDirective {
_empty: U8
}

@ pushes the next RNG value to the stack
struct PushRandDirective {
_empty: U8
}

@ pops a bool off the stack, sets a flag with a specific index to that bool
struct SetFlagDirective {
flagIdx: U8
Expand Down Expand Up @@ -227,6 +237,10 @@ internal port directive_stackCmd(directive: StackCmdDirective) priority 6 assert

internal port directive_pushTime(directive: PushTimeDirective) priority 6 assert

internal port directive_setSeed(directive: SetSeedDirective) priority 6 assert

internal port directive_pushRand(directive: PushRandDirective) priority 6 assert

internal port directive_setFlag(directive: SetFlagDirective) priority 6 assert

internal port directive_getFlag(directive: GetFlagDirective) priority 6 assert
Expand Down
28 changes: 28 additions & 0 deletions Svc/FpySequencer/FpySequencerRunState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,26 @@ Fw::Success FpySequencer::deserializeDirective(const Fpy::Statement& stmt, Direc
}
break;
}
case Fpy::DirectiveId::SET_SEED: {
new (&deserializedDirective.setSeed) FpySequencer_SetSeedDirective();
if (argBuf.getDeserializeSizeLeft() != 0) {
this->log_WARNING_HI_DirectiveDeserializeError(stmt.get_opCode(), this->currentStatementIdx(),
Fw::SerializeStatus::FW_DESERIALIZE_SIZE_MISMATCH,
argBuf.getDeserializeSizeLeft(), argBuf.getSize());
return Fw::Success::FAILURE;
}
break;
}
case Fpy::DirectiveId::PUSH_RAND: {
new (&deserializedDirective.pushRand) FpySequencer_PushRandDirective();
if (argBuf.getDeserializeSizeLeft() != 0) {
this->log_WARNING_HI_DirectiveDeserializeError(stmt.get_opCode(), this->currentStatementIdx(),
Fw::SerializeStatus::FW_DESERIALIZE_SIZE_MISMATCH,
argBuf.getDeserializeSizeLeft(), argBuf.getSize());
return Fw::Success::FAILURE;
}
break;
}
case Fpy::DirectiveId::SET_FLAG: {
new (&deserializedDirective.setFlag) FpySequencer_SetFlagDirective();
status = argBuf.deserializeTo(deserializedDirective.setFlag);
Expand Down Expand Up @@ -600,6 +620,14 @@ void FpySequencer::dispatchDirective(const DirectiveUnion& directive, const Fpy:
this->directive_pushTime_internalInterfaceInvoke(directive.pushTime);
return;
}
case Fpy::DirectiveId::SET_SEED: {
this->directive_setSeed_internalInterfaceInvoke(directive.setSeed);
return;
}
case Fpy::DirectiveId::PUSH_RAND: {
this->directive_pushRand_internalInterfaceInvoke(directive.pushRand);
return;
}
case Fpy::DirectiveId::SET_FLAG: {
this->directive_setFlag_internalInterfaceInvoke(directive.setFlag);
return;
Expand Down
4 changes: 3 additions & 1 deletion Svc/FpySequencer/FpySequencerTypes.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ module Svc {
LOAD_ABS = 74
STORE_ABS = 75
STORE_ABS_CONST_OFFSET = 76
SET_SEED = 77
PUSH_RAND = 78
}

enum DirectiveErrorCode : U8 {
Expand Down Expand Up @@ -180,4 +182,4 @@ module Svc {
footer: Footer
}
}
}
}
21 changes: 21 additions & 0 deletions Svc/FpySequencer/docs/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -1107,3 +1107,24 @@ Stores a value to an absolute address in the stack (used for global variables),
| value | bytes | stack | The value to store (popped from stack top). |

**Requirement:** FPY-SEQ-009

## SET_SEED (77)
Pops a `U32` seed value from the stack and uses it to seed the sequencer's internal PRNG.

| Arg Name | Arg Type | Source | Description |
|----------|----------|--------|-------------|
| seed | U32 | stack | Seed value used to initialize the PRNG |

| Stack Result Type | Description |
| ------------------|-------------|
| N/A | |


## PUSH_RAND (78)
Pushes the next PRNG value to the stack.
If this is called without seed being set beforehand it will be automatically done based on current time.
Note that this function uses C++'s random library so it does not qualify as a CSPRNG and is thus not cryptographically secure.
[](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator)
| Stack Result Type | Description |
| ------------------|-------------|
| U32 | The next pseudorandom 32-bit value from the sequencer's internal PRNG |
90 changes: 90 additions & 0 deletions Svc/FpySequencer/test/ut/FpySequencerTestMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,72 @@ TEST_F(FpySequencerTester, pushTime) {
ASSERT_EQ(err, DirectiveError::STACK_OVERFLOW);
}

TEST_F(FpySequencerTester, pushRand) {
FpySequencer_PushRandDirective directive;
DirectiveError err = DirectiveError::NO_ERROR;
Fw::Time testTime(TimeBase::TB_WORKSTATION_TIME, 7, 123, 456);
setTestTime(testTime);
std::mt19937 expectedRng;
std::seed_seq seedSeq{static_cast<U32>(testTime.getTimeBase()),
static_cast<U32>(testTime.getContext()),
testTime.getSeconds(),
testTime.getUSeconds()};
expectedRng.seed(seedSeq);

tester_get_m_runtime_ptr()->stack.size = 0;
Signal result = tester_pushRand_directiveHandler(directive, err);
ASSERT_EQ(tester_get_m_runtime_ptr()->stack.size, sizeof(U32));
ASSERT_EQ(result, Signal::stmtResponse_success);
ASSERT_EQ(err, DirectiveError::NO_ERROR);
ASSERT_EQ(tester_pop<U32>(), expectedRng());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing function

// make sure subsequent calls advance the existing RNG instead of reseeding on new time
setTestTime(Fw::Time(TimeBase::TB_WORKSTATION_TIME, 99, 999, 999));
result = tester_pushRand_directiveHandler(directive, err);
ASSERT_EQ(result, Signal::stmtResponse_success);
ASSERT_EQ(err, DirectiveError::NO_ERROR);
ASSERT_EQ(tester_pop<U32>(), expectedRng());

// check almost overflow
tester_get_m_runtime_ptr()->stack.size = Fpy::MAX_STACK_SIZE - sizeof(U32);
result = tester_pushRand_directiveHandler(directive, err);
ASSERT_EQ(result, Signal::stmtResponse_success);
ASSERT_EQ(tester_get_m_runtime_ptr()->stack.size, Fpy::MAX_STACK_SIZE);
ASSERT_EQ(err, DirectiveError::NO_ERROR);

// check overflow
tester_get_m_runtime_ptr()->stack.size = Fpy::MAX_STACK_SIZE;
result = tester_pushRand_directiveHandler(directive, err);
ASSERT_EQ(result, Signal::stmtResponse_failure);
ASSERT_EQ(err, DirectiveError::STACK_OVERFLOW);
}

TEST_F(FpySequencerTester, setSeed) {
FpySequencer_SetSeedDirective directive;
DirectiveError err = DirectiveError::NO_ERROR;
const U32 testSeed = 123456789U;
std::mt19937 expectedRng;
expectedRng.seed(testSeed);

tester_push<U32>(testSeed);
Signal result = tester_setSeed_directiveHandler(directive, err);
ASSERT_EQ(result, Signal::stmtResponse_success);
ASSERT_EQ(err, DirectiveError::NO_ERROR);
ASSERT_EQ(tester_get_m_runtime_ptr()->stack.size, 0);

// make sure the explicit seed overrides time-based initialization
setTestTime(Fw::Time(TimeBase::TB_WORKSTATION_TIME, 77, 888, 999));
result = tester_pushRand_directiveHandler(FpySequencer_PushRandDirective(), err);
ASSERT_EQ(result, Signal::stmtResponse_success);
ASSERT_EQ(err, DirectiveError::NO_ERROR);
ASSERT_EQ(tester_pop<U32>(), expectedRng());

// underflow
result = tester_setSeed_directiveHandler(directive, err);
ASSERT_EQ(result, Signal::stmtResponse_failure);
ASSERT_EQ(err, DirectiveError::STACK_UNDERFLOW);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add testers for the deserialize_xyz behavior. see how pushTime does it

TEST_F(FpySequencerTester, getField) {
FpySequencer_GetFieldDirective directive(4, 2); // parent size 3, member size 2
tester_push<U8>(123);
Expand Down Expand Up @@ -4136,6 +4202,30 @@ TEST_F(FpySequencerTester, IntegrationPushTime) {
ASSERT_CMD_RESPONSE(0, get_OPCODE_RUN(), 0, Fw::CmdResponse::OK);
}

TEST_F(FpySequencerTester, IntegrationPushRand) {
allocMem();
// Sequence: PUSH_RAND, DISCARD(sizeof(U32))
add_PUSH_RAND();
add_DISCARD(sizeof(U32));
writeAndRun();
dispatchUntilState(State::IDLE);
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, get_OPCODE_RUN(), 0, Fw::CmdResponse::OK);
}

TEST_F(FpySequencerTester, IntegrationSetSeedPushRand) {
allocMem();
// Sequence: PUSH_VAL(seed), SET_SEED, PUSH_RAND, DISCARD(sizeof(U32))
add_PUSH_VAL<U32>(123456789U);
add_SET_SEED();
add_PUSH_RAND();
add_DISCARD(sizeof(U32));
writeAndRun();
dispatchUntilState(State::IDLE);
ASSERT_CMD_RESPONSE_SIZE(1);
ASSERT_CMD_RESPONSE(0, get_OPCODE_RUN(), 0, Fw::CmdResponse::OK);
}

TEST_F(FpySequencerTester, IntegrationGetField) {
allocMem();
// Push a 2-byte "parent": [0xAA, 0xBB]
Expand Down
18 changes: 18 additions & 0 deletions Svc/FpySequencer/test/ut/FpySequencerTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,14 @@ void FpySequencerTester::add_PUSH_TIME() {
Fw::StatementArgBuffer buf;
addDirective(Fpy::DirectiveId::PUSH_TIME, buf);
}
void FpySequencerTester::add_SET_SEED() {
Fw::StatementArgBuffer buf;
addDirective(Fpy::DirectiveId::SET_SEED, buf);
}
void FpySequencerTester::add_PUSH_RAND() {
Fw::StatementArgBuffer buf;
addDirective(Fpy::DirectiveId::PUSH_RAND, buf);
}
void FpySequencerTester::add_GET_FIELD(const Fpy::StackSizeType parentSize, const Fpy::StackSizeType memberSize) {
add_GET_FIELD(FpySequencer_GetFieldDirective(parentSize, memberSize));
}
Expand Down Expand Up @@ -562,6 +570,16 @@ Signal FpySequencerTester::tester_pushTime_directiveHandler(const FpySequencer_P
return this->cmp.pushTime_directiveHandler(directive, err);
}

Signal FpySequencerTester::tester_setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive,
DirectiveError& err) {
return this->cmp.setSeed_directiveHandler(directive, err);
}

Signal FpySequencerTester::tester_pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive,
DirectiveError& err) {
return this->cmp.pushRand_directiveHandler(directive, err);
}

Signal FpySequencerTester::tester_allocate_directiveHandler(const FpySequencer_AllocateDirective& directive,
DirectiveError& err) {
return this->cmp.allocate_directiveHandler(directive, err);
Expand Down
4 changes: 4 additions & 0 deletions Svc/FpySequencer/test/ut/FpySequencerTester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ class FpySequencerTester : public FpySequencerGTestBase, public ::testing::Test
void add_GET_FLAG(U8 flagIdx);
void add_GET_FLAG(FpySequencer_GetFlagDirective dir);
void add_PUSH_TIME();
void add_SET_SEED();
void add_PUSH_RAND();
void add_GET_FIELD(Fpy::StackSizeType parentSize, Fpy::StackSizeType memberSize);
void add_GET_FIELD(FpySequencer_GetFieldDirective dir);
void add_PEEK();
Expand Down Expand Up @@ -186,6 +188,8 @@ class FpySequencerTester : public FpySequencerGTestBase, public ::testing::Test
Signal tester_storeAbsConstOffset_directiveHandler(const FpySequencer_StoreAbsConstOffsetDirective& directive,
DirectiveError& err);
Signal tester_pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& err);
Signal tester_setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& err);
Signal tester_pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& err);
Signal tester_allocate_directiveHandler(const FpySequencer_AllocateDirective& directive, DirectiveError& err);
Signal tester_loadRel_directiveHandler(const FpySequencer_LoadRelDirective& directive, DirectiveError& err);
Signal tester_storeRelConstOffset_directiveHandler(const FpySequencer_StoreRelConstOffsetDirective& directive,
Expand Down
Loading