F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
ActivePhaser.cpp
Go to the documentation of this file.
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 
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
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 
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();
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
U32 start
static const U32 MAX_CHILDREN
void configure(U32 cycle_ticks)
void register_phased(FwIndexType port, U32 length, U32 start=DONT_CARE, U32 userContext=DONT_CARE)
PlatformSizeType FwSizeType
U32 context
void unLock()
unlock the mutex and assert success
Definition: Mutex.cpp:41
FinishStatus
Finish status.
void PhaserMemberOut_out(FwIndexType portNum, U32 context) const
Invoke output port PhaserMemberOut.
PhaserStateEntry entries[MAX_CHILDREN]
FwIndexType port
ActivePhaser(const char *const compName)
Construct ActivePhaser object.
void init()
Object initializer.
Definition: ObjBase.cpp:24
configuration for phasing
U32 length
PhaserContextType contextType
bool started
~ActivePhaser()
Destroy ActivePhaser object.
void Tick_internalInterfaceInvoke()
Internal interface base-class function for Tick.
static const U32 DONT_CARE
bool isConnected_PhaserMemberOut_OutputPort(FwIndexType portNum) const
Auto-generated base for ActivePhaser component.
PlatformIndexType FwIndexType
static constexpr FwIndexType getNum_PhaserMemberOut_OutputPorts()
RateGroupDivider component implementation.
void start(FwTaskPriorityType priority=Os::Task::TASK_PRIORITY_DEFAULT, FwSizeType stackSize=Os::Task::TASK_DEFAULT, FwSizeType cpuAffinity=Os::Task::TASK_DEFAULT, FwTaskIdType identifier=static_cast< FwTaskIdType >(Os::Task::TASK_DEFAULT))
called by instantiator when task is to be started
U32 used
The number of registered tasks (the last registered task is at used - 1)
U32 current
The current child task entry index.
#define FW_ASSERT(...)
Definition: Assert.hpp:14
void lock()
lock the mutex and assert success
Definition: Mutex.cpp:34
void log_WARNING_HI_MissedDeadline(FwIndexType p, U32 start, U32 length, U32 ticks)