| 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 | 8 | ActivePhaser ::ActivePhaser(const char* const compName) | |
| 23 | : ActivePhaserComponentBase(compName), | ||
| 24 | 8 | m_cycle(0), | |
| 25 | 8 | m_ticks(0xFFFFFFFF), | |
| 26 | 8 | m_ticks_rollover(1), // Start at 1. Will be multiplied by each context to find some common multiple. | |
| 27 | 8 | m_last_start_ticks(0), | |
| 28 | 8 | m_last_cycle_ticks(0), | |
| 29 |
1/1✓ Branch 9 taken 8 times.
|
16 | m_cycle_count(0) { |
| 30 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
8 | (void)::memset(&m_state, 0, sizeof(m_state)); // Zero-out the whole configuration table |
| 31 | 8 | } | |
| 32 | |||
| 33 | 8 | void ActivePhaser ::init(const FwSizeType queueDepth, const FwIndexType instance) { | |
| 34 | 8 | FW_ASSERT(queueDepth == 1, static_cast<FwAssertArgType>( | |
| 35 | queueDepth)); // Dependent on queue-depth of one to prevent a rush to catch up | ||
| 36 | 8 | ActivePhaserComponentBase::init(1, instance); | |
| 37 | 8 | } | |
| 38 | |||
| 39 | 8 | void ActivePhaser ::configure(U32 cycle_ticks) { | |
| 40 | 8 | FW_ASSERT(cycle_ticks != 0); | |
| 41 | 8 | m_cycle = cycle_ticks; | |
| 42 | 8 | } | |
| 43 | |||
| 44 | 35 | void ActivePhaser ::register_phased(FwIndexType port, U32 length, U32 start, U32 userContext) { | |
| 45 | 35 | FW_ASSERT(m_cycle != 0); | |
| 46 | 35 | 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 |
2/2✓ Branch 4 taken 27 times.
✓ Branch 5 taken 8 times.
|
35 | if (m_state.used > 0) { |
| 50 | 27 | const PhaserStateEntry& previous = m_state.entries[m_state.used - 1]; | |
| 51 | 27 | 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 | 27 | 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 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 15 times.
|
27 | 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 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 6 times.
|
35 | start = (start == DONT_CARE) ? 0 : start; |
| 63 | 35 | PhaserStateEntry& entry = m_state.entries[m_state.used]; | |
| 64 | |||
| 65 | // Check assertions on the ports | ||
| 66 | 35 | FW_ASSERT(port < getNum_PhaserMemberOut_OutputPorts(), static_cast<FwAssertArgType>(port)); | |
| 67 | 35 | FW_ASSERT(isConnected_PhaserMemberOut_OutputPort(port), static_cast<FwAssertArgType>(port)); | |
| 68 | 35 | FW_ASSERT(length <= m_cycle, static_cast<FwAssertArgType>(length), static_cast<FwAssertArgType>(m_cycle)); | |
| 69 | 35 | FW_ASSERT(start <= m_cycle - length, static_cast<FwAssertArgType>(start), static_cast<FwAssertArgType>(length), | |
| 70 | static_cast<FwAssertArgType>(m_cycle)); | ||
| 71 | 35 | FW_ASSERT(userContext > m_cycle, static_cast<FwAssertArgType>(userContext), static_cast<FwAssertArgType>(m_cycle)); | |
| 72 | |||
| 73 | 35 | entry.port = port; | |
| 74 | 35 | entry.start = start; | |
| 75 | 35 | 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 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 25 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 10 times.
|
35 | entry.context = (userContext != DONT_CARE) ? userContext / m_cycle : getNextContext(port); |
| 83 | // Update some common multiple of all contexts | ||
| 84 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 25 times.
|
35 | if (userContext != DONT_CARE) { |
| 85 | // Check for overflow before multiply | ||
| 86 | 10 | FW_ASSERT(std::numeric_limits<U32>::max() / m_ticks_rollover >= entry.context); | |
| 87 | 10 | m_ticks_rollover *= entry.context; | |
| 88 | } | ||
| 89 | |||
| 90 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 25 times.
|
35 | entry.contextType = (userContext != DONT_CARE) ? PhaserContextType::COUNT : PhaserContextType::SEQUENTIAL; |
| 91 | 35 | entry.started = false; | |
| 92 | 35 | m_state.used += 1; | |
| 93 | 35 | } | |
| 94 | |||
| 95 | 16 | ActivePhaser ::~ActivePhaser() {} | |
| 96 | |||
| 97 | // ---------------------------------------------------------------------- | ||
| 98 | // Handler implementations for typed input ports | ||
| 99 | // ---------------------------------------------------------------------- | ||
| 100 | |||
| 101 | 98800 | void ActivePhaser ::CycleIn_handler(FwIndexType portNum, Os::RawTime& cycleStart) { | |
| 102 | 98800 | m_lock.lock(); | |
| 103 | 98800 | m_ticks += 1; | |
| 104 | 98800 | m_lock.unLock(); | |
| 105 | 98800 | this->Tick_internalInterfaceInvoke(); | |
| 106 | 98800 | } | |
| 107 | |||
| 108 | // ---------------------------------------------------------------------- | ||
| 109 | // Handler implementations for user-defined internal interfaces | ||
| 110 | // ---------------------------------------------------------------------- | ||
| 111 | |||
| 112 | 38200 | void ActivePhaser ::Tick_internalInterfaceHandler() { | |
| 113 | 38200 | FW_ASSERT(m_state.current <= m_state.used, static_cast<FwAssertArgType>(m_state.current), | |
| 114 | static_cast<FwAssertArgType>(m_state.used)); | ||
| 115 | 38200 | m_lock.lock(); | |
| 116 | 38200 | U32 full_ticks = m_ticks; | |
| 117 | 38200 | m_lock.unLock(); | |
| 118 | |||
| 119 | // If the cycle is over, wait for the cycle to end before restarting | ||
| 120 |
6/6✓ Branch 8 taken 1193 times.
✓ Branch 9 taken 37007 times.
✓ Branch 18 taken 793 times.
✓ Branch 19 taken 400 times.
✓ Branch 20 taken 793 times.
✓ Branch 21 taken 37407 times.
|
38200 | if ((this->timeInCycle(full_ticks) >= m_cycle) && (m_state.current == m_state.used)) { |
| 121 | 793 | m_last_cycle_ticks = full_ticks; | |
| 122 | // Increment cycle count modulo some common factor of all contexts | ||
| 123 | 793 | FW_ASSERT(m_ticks_rollover != 0); | |
| 124 |
1/2✗ Branch 8 not taken.
✓ Branch 9 taken 793 times.
|
793 | m_cycle_count = (m_cycle_count + 1) % m_ticks_rollover; |
| 125 | 793 | m_state.current = 0; // Back to processing the first task. | |
| 126 | } | ||
| 127 | // Run the next child if the finishing child wast not late | ||
| 128 |
2/2✓ Branch 4 taken 36100 times.
✓ Branch 5 taken 2100 times.
|
38200 | if (finishChild(full_ticks) != ActivePhaser::FinishStatus::LATE) { |
| 129 | 36100 | startChild(full_ticks); | |
| 130 | } | ||
| 131 | 38200 | } | |
| 132 | |||
| 133 | 38200 | ActivePhaser::FinishStatus ActivePhaser ::finishChild(U32 full_ticks) { | |
| 134 | // Guard against finishing improperly | ||
| 135 |
5/6✓ Branch 8 taken 7100 times.
✓ Branch 9 taken 31100 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 7100 times.
✓ Branch 19 taken 3600 times.
✓ Branch 20 taken 3500 times.
|
38200 | if ((m_state.current >= m_state.used) || (not m_state.entries[m_state.current].started)) { |
| 136 | 34700 | 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 |
1/2✗ Branch 8 not taken.
✓ Branch 9 taken 3500 times.
|
3500 | PhaserStateEntry& entry = m_state.entries[(m_state.current % m_state.used)]; |
| 143 | 3500 | const U32 execution_time = full_ticks - m_last_start_ticks; | |
| 144 | 3500 | const U32 expected_time = entry.length; | |
| 145 | |||
| 146 | // Mark entry as done | ||
| 147 | 3500 | entry.started = false; | |
| 148 | // Increment the current task index if it has not reached used, i.e., the max index registered. | ||
| 149 |
1/2✗ Branch 8 not taken.
✓ Branch 9 taken 3500 times.
|
3500 | 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 |
2/2✓ Branch 0 taken 2100 times.
✓ Branch 1 taken 1400 times.
|
3500 | if (execution_time > expected_time) { |
| 152 | 2100 | this->log_WARNING_HI_MissedDeadline(entry.port, entry.start, entry.length, (execution_time - expected_time)); | |
| 153 | 2100 | return ActivePhaser::FinishStatus::LATE; | |
| 154 | } | ||
| 155 | // If no overrun, proceed with the next child task. | ||
| 156 | 1400 | return ActivePhaser::FinishStatus::ON_TIME; | |
| 157 | } | ||
| 158 | |||
| 159 | 36100 | void ActivePhaser ::startChild(U32 full_ticks) { | |
| 160 | // Guard against starting improperly | ||
| 161 | 36100 | if ((m_state.current >= m_state.used) // Invalid. Current index surpasses the indices of registered tasks. | |
| 162 |
2/2✓ Branch 8 taken 3500 times.
✓ Branch 9 taken 1200 times.
|
9400 | || (m_state.entries[m_state.current].start > |
| 163 | 4700 | timeInCycle(full_ticks)) // Current time has not reached the intended start time. | |
| 164 |
6/8✓ Branch 0 taken 4700 times.
✓ Branch 1 taken 31400 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3500 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3500 times.
✓ Branch 13 taken 32600 times.
✓ Branch 14 taken 3500 times.
|
40800 | || m_state.entries[m_state.current].started) // The current child task has already started. |
| 165 | { | ||
| 166 | 32600 | return; | |
| 167 | } | ||
| 168 |
1/2✗ Branch 8 not taken.
✓ Branch 9 taken 3500 times.
|
3500 | 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 | 3500 | U32 context = entry.context; | |
| 173 |
3/4✗ Branch 1 not taken.
✓ Branch 2 taken 3500 times.
✓ Branch 3 taken 1000 times.
✓ Branch 4 taken 2500 times.
|
3500 | if (entry.contextType != SEQUENTIAL) { |
| 174 | 1000 | FW_ASSERT(entry.context != 0, static_cast<FwAssertArgType>(entry.port)); | |
| 175 |
1/2✗ Branch 6 not taken.
✓ Branch 7 taken 1000 times.
|
1000 | context = m_cycle_count % entry.context; |
| 176 | } | ||
| 177 | 3500 | entry.started = true; | |
| 178 | 3500 | m_last_start_ticks = full_ticks; | |
| 179 | 3500 | this->PhaserMemberOut_out(entry.port, context); | |
| 180 | } | ||
| 181 | |||
| 182 | 25 | U32 ActivePhaser ::getNextContext(FwIndexType port) { | |
| 183 | 25 | 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 |
2/2✓ Branch 4 taken 43 times.
✓ Branch 5 taken 25 times.
|
68 | for (U32 i = 0; i < m_state.used; i++) { |
| 188 |
2/2✓ Branch 4 taken 16 times.
✓ Branch 5 taken 27 times.
|
43 | if (m_state.entries[i].port == port) { |
| 189 | 16 | context = m_state.entries[i].context + 1; | |
| 190 | } | ||
| 191 | } | ||
| 192 | 25 | return context; | |
| 193 | } | ||
| 194 | |||
| 195 | 42900 | U32 ActivePhaser ::timeInCycle(U32 full_ticks) { | |
| 196 | 42900 | return (full_ticks - m_last_cycle_ticks); | |
| 197 | } | ||
| 198 | |||
| 199 | } // namespace Svc | ||
| 200 |