GCC Code Coverage Report


Directory: Svc/ActivePhaser/
File: ActivePhaser.cpp
Date: 2026-09-03 21:16:10
Exec Total Coverage
Lines: 97 97 100.0%
Functions: 11 11 100.0%
Branches: 50 61 82.0%

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