| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * \author: Tim Canham | ||
| 3 | * \file: | ||
| 4 | * \brief | ||
| 5 | * | ||
| 6 | * This file implements the PassiveRateGroup component, | ||
| 7 | * which invokes a set of components the comprise the rate group. | ||
| 8 | * | ||
| 9 | * Copyright 2014-2015, by the California Institute of Technology. | ||
| 10 | * ALL RIGHTS RESERVED. United States Government Sponsorship | ||
| 11 | * acknowledged. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <Fw/FPrimeBasicTypes.hpp> | ||
| 15 | #include <Fw/Types/Assert.hpp> | ||
| 16 | #include <Os/Console.hpp> | ||
| 17 | #include <Svc/PassiveRateGroup/PassiveRateGroup.hpp> | ||
| 18 | #include <config/PassiveRateGroupCfg.hpp> | ||
| 19 | #include "config/FwSizeTypeAliasAc.h" | ||
| 20 | |||
| 21 | namespace Svc { | ||
| 22 | ✗ | PassiveRateGroup::PassiveRateGroup(const char* compName) | |
| 23 | : PassiveRateGroupComponentBase(compName), | ||
| 24 | ✗ | m_cycles(0), | |
| 25 | ✗ | m_maxTime(0), | |
| 26 | ✗ | m_portCycleTimeHWMUsec{}, // Zero-initialize atomic array | |
| 27 | ✗ | m_rawTimeSource(Os::RAWTIME_DEFAULT), | |
| 28 | ✗ | m_numContexts(0) {} | |
| 29 | |||
| 30 | ✗ | PassiveRateGroup::~PassiveRateGroup() {} | |
| 31 | |||
| 32 | ✗ | Os::RawTime PassiveRateGroup::createRawTime() const { | |
| 33 | ✗ | return Os::RawTime(this->m_rawTimeSource); | |
| 34 | } | ||
| 35 | |||
| 36 | ✗ | void PassiveRateGroup::configure(const ContextArray& contexts, const Os::RawTimeSource rawTimeSource) { | |
| 37 | static_assert(FW_NUM_ARRAY_ELEMENTS(m_contexts) == NUM_RATEGROUPMEMBEROUT_OUTPUT_PORTS, | ||
| 38 | "Context table size must match the number of rate group member output ports"); | ||
| 39 | |||
| 40 | ✗ | this->m_numContexts = CONNECTION_COUNT_MAX; | |
| 41 | // copy context values | ||
| 42 | ✗ | for (FwIndexType entry = 0; entry < this->m_numContexts; entry++) { | |
| 43 | ✗ | this->m_contexts[entry] = contexts[static_cast<FwSizeType>(entry)]; | |
| 44 | } | ||
| 45 | |||
| 46 | ✗ | this->m_rawTimeSource = rawTimeSource; | |
| 47 | ✗ | } | |
| 48 | |||
| 49 | ✗ | void PassiveRateGroup::configure(const U32 contexts[], const FwIndexType numContexts) { | |
| 50 | ✗ | FW_ASSERT(contexts != nullptr); | |
| 51 | ✗ | FW_ASSERT(numContexts == this->getNum_RateGroupMemberOut_OutputPorts(), static_cast<FwAssertArgType>(numContexts), | |
| 52 | static_cast<FwAssertArgType>(this->getNum_RateGroupMemberOut_OutputPorts())); | ||
| 53 | |||
| 54 | ✗ | ContextArray contextArray; | |
| 55 | ✗ | for (FwIndexType entry = 0; entry < numContexts; entry++) { | |
| 56 | ✗ | contextArray[static_cast<FwSizeType>(entry)] = static_cast<U32>(contexts[entry]); | |
| 57 | } | ||
| 58 | ✗ | this->configure(contextArray); | |
| 59 | ✗ | } | |
| 60 | |||
| 61 | ✗ | void PassiveRateGroup::CycleIn_handler(FwIndexType portNum, Os::RawTime& cycleStart) { | |
| 62 | ✗ | Os::RawTime endTime = this->createRawTime(); | |
| 63 | ✗ | FW_ASSERT(this->m_numContexts != 0); | |
| 64 | |||
| 65 | // Pre-allocate RawTime objects outside loop to avoid repeated constructor calls | ||
| 66 | ✗ | Os::RawTime portStart = this->createRawTime(); | |
| 67 | ✗ | Os::RawTime portEnd = this->createRawTime(); | |
| 68 | ✗ | PassiveRateGroup_CycleTime portTimes; | |
| 69 | |||
| 70 | // invoke any members of the rate group | ||
| 71 | ✗ | for (FwIndexType port = 0; port < this->getNum_RateGroupMemberOut_OutputPorts(); port++) { | |
| 72 | ✗ | if (this->isConnected_RateGroupMemberOut_OutputPort(port)) { | |
| 73 | if (Svc::PassiveRateGroupCfg::PortCycleTime) { | ||
| 74 | ✗ | (void)portStart.now(); | |
| 75 | } | ||
| 76 | |||
| 77 | ✗ | this->RateGroupMemberOut_out(port, this->m_contexts[port]); | |
| 78 | |||
| 79 | if (Svc::PassiveRateGroupCfg::PortCycleTime) { | ||
| 80 | ✗ | (void)portEnd.now(); | |
| 81 | U32 cycleTime; | ||
| 82 | ✗ | (void)portEnd.getDiffUsec(portStart, cycleTime); | |
| 83 | ✗ | portTimes[static_cast<FwSizeType>(port)] = cycleTime; | |
| 84 | // Update high water mark if current cycle time exceeds it (lock-free atomic) | ||
| 85 | U32 currentHWM = | ||
| 86 | ✗ | this->m_portCycleTimeHWMUsec[static_cast<FwSizeType>(port)].load(std::memory_order_relaxed); | |
| 87 | ✗ | while (cycleTime > currentHWM) { | |
| 88 | ✗ | if (this->m_portCycleTimeHWMUsec[static_cast<FwSizeType>(port)].compare_exchange_weak( | |
| 89 | currentHWM, cycleTime, std::memory_order_relaxed)) { | ||
| 90 | ✗ | break; | |
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | // grab timer for endTime of cycle | ||
| 98 | ✗ | (void)endTime.now(); | |
| 99 | |||
| 100 | // get rate group execution time | ||
| 101 | U32 cycleTime; | ||
| 102 | // Cast to void as the only possible error is overflow, which we can't handle other | ||
| 103 | // than capping cycleTime to max value of U32 (which is done in getDiffUsec anyways) | ||
| 104 | ✗ | (void)endTime.getDiffUsec(cycleStart, cycleTime); | |
| 105 | |||
| 106 | // Update max time atomically (lock-free, ISR-safe) | ||
| 107 | ✗ | U32 currentMax = this->m_maxTime.load(std::memory_order_relaxed); | |
| 108 | ✗ | while (cycleTime > currentMax) { | |
| 109 | ✗ | if (this->m_maxTime.compare_exchange_weak(currentMax, cycleTime, std::memory_order_relaxed)) { | |
| 110 | ✗ | break; | |
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | ✗ | U32 maxTime = this->m_maxTime.load(std::memory_order_relaxed); | |
| 115 | ✗ | U32 cycles = ++this->m_cycles; | |
| 116 | |||
| 117 | if (Svc::PassiveRateGroupCfg::PortCycleTime) { | ||
| 118 | // Copy atomic array to telemetry structure for sending | ||
| 119 | ✗ | PassiveRateGroup_CycleTime portCycleTimeHWM; | |
| 120 | ✗ | for (FwIndexType port = 0; port < this->getNum_RateGroupMemberOut_OutputPorts(); port++) { | |
| 121 | ✗ | portCycleTimeHWM[static_cast<FwSizeType>(port)] = | |
| 122 | ✗ | this->m_portCycleTimeHWMUsec[static_cast<FwSizeType>(port)].load(std::memory_order_relaxed); | |
| 123 | } | ||
| 124 | ✗ | this->tlmWrite_PortCycleTime(portTimes); | |
| 125 | ✗ | this->tlmWrite_PortCycleTimeHWM(portCycleTimeHWM); | |
| 126 | ✗ | } | |
| 127 | |||
| 128 | ✗ | this->tlmWrite_MaxCycleTime(maxTime); | |
| 129 | ✗ | this->tlmWrite_CycleTime(cycleTime); | |
| 130 | ✗ | this->tlmWrite_CycleCount(cycles); | |
| 131 | ✗ | } | |
| 132 | |||
| 133 | ✗ | void PassiveRateGroup::CLEAR_STATISTICS_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { | |
| 134 | // Clear max cycle time (lock-free atomic) | ||
| 135 | ✗ | this->m_maxTime.store(0, std::memory_order_relaxed); | |
| 136 | // Note: m_cycles is intentionally NOT cleared - it's a running total | ||
| 137 | |||
| 138 | // Clear all port cycle time high water marks (lock-free atomic) | ||
| 139 | ✗ | for (FwIndexType port = 0; port < this->getNum_RateGroupMemberOut_OutputPorts(); port++) { | |
| 140 | ✗ | this->m_portCycleTimeHWMUsec[static_cast<FwSizeType>(port)].store(0, std::memory_order_relaxed); | |
| 141 | } | ||
| 142 | |||
| 143 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); | |
| 144 | ✗ | } | |
| 145 | |||
| 146 | } // namespace Svc | ||
| 147 |