GCC Code Coverage Report


Directory: ./
File: Svc/ActivePhaser/ActivePhaser.cpp
Date: 2026-09-03 22:12:29
Exec Total Coverage
Lines: 0 97 0.0%
Functions: 0 11 0.0%
Branches: 0 43 0.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 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