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