| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title ActivePhaser.cpp | ||
| 3 | // \author mstarch | ||
| 4 | // \brief cpp file for ActivePhaser component implementation class | ||
| 5 | // | ||
| 6 | // \copyright | ||
| 7 | // Copyright 2009-2015, by the California Institute of Technology. | ||
| 8 | // ALL RIGHTS RESERVED. United States Government Sponsorship | ||
| 9 | // acknowledged. | ||
| 10 | // | ||
| 11 | // ====================================================================== | ||
| 12 | |||
| 13 | #include "Svc/ActivePhaser/ActivePhaser.hpp" | ||
| 14 | #include <cstring> | ||
| 15 | |||
| 16 | namespace Svc { | ||
| 17 | |||
| 18 | // ---------------------------------------------------------------------- | ||
| 19 | // Component construction and destruction | ||
| 20 | // ---------------------------------------------------------------------- | ||
| 21 | |||
| 22 | ✗ | ActivePhaser ::ActivePhaser(const char* const compName) | |
| 23 | : ActivePhaserComponentBase(compName), | ||
| 24 | ✗ | m_cycle(0), | |
| 25 | ✗ | m_ticks(0xFFFFFFFF), | |
| 26 | ✗ | m_ticks_rollover(1), // Start at 1. Will be multiplied by each context to find some common multiple. | |
| 27 | ✗ | m_last_start_ticks(0), | |
| 28 | ✗ | m_last_cycle_ticks(0), | |
| 29 | ✗ | m_cycle_count(0) { | |
| 30 | ✗ | (void)::memset(&m_state, 0, sizeof(m_state)); // Zero-out the whole configuration table | |
| 31 | ✗ | } | |
| 32 | |||
| 33 | ✗ | void ActivePhaser ::init(const FwSizeType queueDepth, const FwIndexType instance) { | |
| 34 | ✗ | FW_ASSERT(queueDepth == 1, static_cast<FwAssertArgType>( | |
| 35 | queueDepth)); // Dependent on queue-depth of one to prevent a rush to catch up | ||
| 36 | ✗ | ActivePhaserComponentBase::init(1, instance); | |
| 37 | ✗ | } | |
| 38 | |||
| 39 | ✗ | void ActivePhaser ::configure(U32 cycle_ticks) { | |
| 40 | ✗ | FW_ASSERT(cycle_ticks != 0); | |
| 41 | ✗ | m_cycle = cycle_ticks; | |
| 42 | ✗ | } | |
| 43 | |||
| 44 | ✗ | void ActivePhaser ::register_phased(FwIndexType port, U32 length, U32 start, U32 userContext) { | |
| 45 | ✗ | FW_ASSERT(m_cycle != 0); | |
| 46 | ✗ | FW_ASSERT(m_state.used < MAX_CHILDREN, static_cast<FwAssertArgType>(m_state.used), | |
| 47 | static_cast<FwAssertArgType>(MAX_CHILDREN)); | ||
| 48 | // Additional checks when there are previous entries | ||
| 49 | ✗ | if (m_state.used > 0) { | |
| 50 | ✗ | const PhaserStateEntry& previous = m_state.entries[m_state.used - 1]; | |
| 51 | ✗ | FW_ASSERT((previous.start + previous.length) <= start, static_cast<FwAssertArgType>(m_state.used), | |
| 52 | static_cast<FwAssertArgType>(previous.start), | ||
| 53 | static_cast<FwAssertArgType>(start)); // Must start after previous entry | ||
| 54 | ✗ | FW_ASSERT(previous.start < start, static_cast<FwAssertArgType>(m_state.used), | |
| 55 | static_cast<FwAssertArgType>(previous.start), | ||
| 56 | static_cast<FwAssertArgType>(start)); // Must start after previous entry | ||
| 57 | // Calculate the next start position when DONT_CARE is specified. | ||
| 58 | ✗ | start = (start == DONT_CARE) ? previous.start + previous.length : start; | |
| 59 | } | ||
| 60 | // If start is DONT_CARE and does not inherit from the end of the previous task, | ||
| 61 | // which happens when registering the first task, set start to 0. | ||
| 62 | ✗ | start = (start == DONT_CARE) ? 0 : start; | |
| 63 | ✗ | PhaserStateEntry& entry = m_state.entries[m_state.used]; | |
| 64 | |||
| 65 | // Check assertions on the ports | ||
| 66 | ✗ | FW_ASSERT(port < getNum_PhaserMemberOut_OutputPorts(), static_cast<FwAssertArgType>(port)); | |
| 67 | ✗ | FW_ASSERT(isConnected_PhaserMemberOut_OutputPort(port), static_cast<FwAssertArgType>(port)); | |
| 68 | ✗ | FW_ASSERT(length <= m_cycle, static_cast<FwAssertArgType>(length), static_cast<FwAssertArgType>(m_cycle)); | |
| 69 | ✗ | FW_ASSERT(start <= m_cycle - length, static_cast<FwAssertArgType>(start), static_cast<FwAssertArgType>(length), | |
| 70 | static_cast<FwAssertArgType>(m_cycle)); | ||
| 71 | ✗ | FW_ASSERT(userContext > m_cycle, static_cast<FwAssertArgType>(userContext), static_cast<FwAssertArgType>(m_cycle)); | |
| 72 | |||
| 73 | ✗ | entry.port = port; | |
| 74 | ✗ | entry.start = start; | |
| 75 | ✗ | entry.length = length; | |
| 76 | // By default, userContext is DONT_CARE, which means the context type is SEQUENTIAL | ||
| 77 | // and a port's context value by default increments every time it is registered. | ||
| 78 | // If a value is given to userContext, the context type becomes COUNT, and | ||
| 79 | // entry.context represents the ratio between userContext and the phaser cycle. | ||
| 80 | // userContext must be greater than the phaser cycle. | ||
| 81 | // Example: If userContext == 2000 and m_cycle == 100, then entry.context == 20 while contextType == COUNT. | ||
| 82 | ✗ | entry.context = (userContext != DONT_CARE) ? userContext / m_cycle : getNextContext(port); | |
| 83 | // Update some common multiple of all contexts | ||
| 84 | ✗ | if (userContext != DONT_CARE) { | |
| 85 | // Check for overflow before multiply | ||
| 86 | ✗ | FW_ASSERT(std::numeric_limits<U32>::max() / m_ticks_rollover >= entry.context); | |
| 87 | ✗ | m_ticks_rollover *= entry.context; | |
| 88 | } | ||
| 89 | |||
| 90 | ✗ | entry.contextType = (userContext != DONT_CARE) ? PhaserContextType::COUNT : PhaserContextType::SEQUENTIAL; | |
| 91 | ✗ | entry.started = false; | |
| 92 | ✗ | m_state.used += 1; | |
| 93 | ✗ | } | |
| 94 | |||
| 95 | ✗ | ActivePhaser ::~ActivePhaser() {} | |
| 96 | |||
| 97 | // ---------------------------------------------------------------------- | ||
| 98 | // Handler implementations for typed input ports | ||
| 99 | // ---------------------------------------------------------------------- | ||
| 100 | |||
| 101 | ✗ | void ActivePhaser ::CycleIn_handler(FwIndexType portNum, Os::RawTime& cycleStart) { | |
| 102 | ✗ | m_lock.lock(); | |
| 103 | ✗ | m_ticks += 1; | |
| 104 | ✗ | m_lock.unLock(); | |
| 105 | ✗ | this->Tick_internalInterfaceInvoke(); | |
| 106 | ✗ | } | |
| 107 | |||
| 108 | // ---------------------------------------------------------------------- | ||
| 109 | // Handler implementations for user-defined internal interfaces | ||
| 110 | // ---------------------------------------------------------------------- | ||
| 111 | |||
| 112 | ✗ | void ActivePhaser ::Tick_internalInterfaceHandler() { | |
| 113 | ✗ | FW_ASSERT(m_state.current <= m_state.used, static_cast<FwAssertArgType>(m_state.current), | |
| 114 | static_cast<FwAssertArgType>(m_state.used)); | ||
| 115 | ✗ | m_lock.lock(); | |
| 116 | ✗ | U32 full_ticks = m_ticks; | |
| 117 | ✗ | m_lock.unLock(); | |
| 118 | |||
| 119 | // If the cycle is over, wait for the cycle to end before restarting | ||
| 120 | ✗ | if ((this->timeInCycle(full_ticks) >= m_cycle) && (m_state.current == m_state.used)) { | |
| 121 | ✗ | m_last_cycle_ticks = full_ticks; | |
| 122 | // Increment cycle count modulo some common factor of all contexts | ||
| 123 | ✗ | FW_ASSERT(m_ticks_rollover != 0); | |
| 124 | ✗ | m_cycle_count = (m_cycle_count + 1) % m_ticks_rollover; | |
| 125 | ✗ | m_state.current = 0; // Back to processing the first task. | |
| 126 | } | ||
| 127 | // Run the next child if the finishing child wast not late | ||
| 128 | ✗ | if (finishChild(full_ticks) != ActivePhaser::FinishStatus::LATE) { | |
| 129 | ✗ | startChild(full_ticks); | |
| 130 | } | ||
| 131 | ✗ | } | |
| 132 | |||
| 133 | ✗ | ActivePhaser::FinishStatus ActivePhaser ::finishChild(U32 full_ticks) { | |
| 134 | // Guard against finishing improperly | ||
| 135 | ✗ | if ((m_state.current >= m_state.used) || (not m_state.entries[m_state.current].started)) { | |
| 136 | ✗ | return ActivePhaser::FinishStatus::UNKNOWN; | |
| 137 | } | ||
| 138 | // Only reachable here when current has not reached used | ||
| 139 | // and the current task was previously marked started. | ||
| 140 | // Now the task can be marked as done and the next task | ||
| 141 | // can be launched. | ||
| 142 | ✗ | PhaserStateEntry& entry = m_state.entries[(m_state.current % m_state.used)]; | |
| 143 | ✗ | const U32 execution_time = full_ticks - m_last_start_ticks; | |
| 144 | ✗ | const U32 expected_time = entry.length; | |
| 145 | |||
| 146 | // Mark entry as done | ||
| 147 | ✗ | entry.started = false; | |
| 148 | // Increment the current task index if it has not reached used, i.e., the max index registered. | ||
| 149 | ✗ | m_state.current = (m_state.current == m_state.used) ? m_state.used : (m_state.current + 1); | |
| 150 | // Check for overrun in timing. If a deadline violation is detected report this child as LATE | ||
| 151 | ✗ | if (execution_time > expected_time) { | |
| 152 | ✗ | this->log_WARNING_HI_MissedDeadline(entry.port, entry.start, entry.length, (execution_time - expected_time)); | |
| 153 | ✗ | return ActivePhaser::FinishStatus::LATE; | |
| 154 | } | ||
| 155 | // If no overrun, proceed with the next child task. | ||
| 156 | ✗ | return ActivePhaser::FinishStatus::ON_TIME; | |
| 157 | } | ||
| 158 | |||
| 159 | ✗ | void ActivePhaser ::startChild(U32 full_ticks) { | |
| 160 | // Guard against starting improperly | ||
| 161 | ✗ | if ((m_state.current >= m_state.used) // Invalid. Current index surpasses the indices of registered tasks. | |
| 162 | ✗ | || (m_state.entries[m_state.current].start > | |
| 163 | ✗ | timeInCycle(full_ticks)) // Current time has not reached the intended start time. | |
| 164 | ✗ | || m_state.entries[m_state.current].started) // The current child task has already started. | |
| 165 | { | ||
| 166 | ✗ | return; | |
| 167 | } | ||
| 168 | ✗ | PhaserStateEntry& entry = m_state.entries[(m_state.current % m_state.used)]; | |
| 169 | // If context type is SEQUENTIAL, entry.context stores the registration index of this port among the entries | ||
| 170 | // registered to the same port, fixed at registration time. If context type is COUNT, entry.context stores the | ||
| 171 | // number of phaser cycles elapsed within a user-specified time window. | ||
| 172 | ✗ | U32 context = entry.context; | |
| 173 | ✗ | if (entry.contextType != SEQUENTIAL) { | |
| 174 | ✗ | FW_ASSERT(entry.context != 0, static_cast<FwAssertArgType>(entry.port)); | |
| 175 | ✗ | context = m_cycle_count % entry.context; | |
| 176 | } | ||
| 177 | ✗ | entry.started = true; | |
| 178 | ✗ | m_last_start_ticks = full_ticks; | |
| 179 | ✗ | this->PhaserMemberOut_out(entry.port, context); | |
| 180 | } | ||
| 181 | |||
| 182 | ✗ | U32 ActivePhaser ::getNextContext(FwIndexType port) { | |
| 183 | ✗ | U32 context = 0; | |
| 184 | // Linear search to see if the entry's port matches the target port, | ||
| 185 | // if so, increment the context. | ||
| 186 | // Unlikely to overflow because this happens during registration. | ||
| 187 | ✗ | for (U32 i = 0; i < m_state.used; i++) { | |
| 188 | ✗ | if (m_state.entries[i].port == port) { | |
| 189 | ✗ | context = m_state.entries[i].context + 1; | |
| 190 | } | ||
| 191 | } | ||
| 192 | ✗ | return context; | |
| 193 | } | ||
| 194 | |||
| 195 | ✗ | U32 ActivePhaser ::timeInCycle(U32 full_ticks) { | |
| 196 | ✗ | return (full_ticks - m_last_cycle_ticks); | |
| 197 | } | ||
| 198 | |||
| 199 | } // namespace Svc | ||
| 200 |