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  ::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 context) {
45  FW_ASSERT(m_cycle != 0);
46  FW_ASSERT(m_state.used < 0xFFFF, static_cast<FwAssertArgType>(m_state.used));
47  // Additional checks when there are previous entries
48  if (m_state.used > 0) {
49  const PhaserStateEntry& previous = m_state.entries[m_state.used - 1];
50  FW_ASSERT((previous.start + previous.length - 1) < start, static_cast<FwAssertArgType>(m_state.used),
51  static_cast<FwAssertArgType>(previous.start),
52  static_cast<FwAssertArgType>(start)); // Must start after previous entry
53  FW_ASSERT(previous.start < start, static_cast<FwAssertArgType>(m_state.used),
54  static_cast<FwAssertArgType>(previous.start),
55  static_cast<FwAssertArgType>(start)); // Must start after previous entry
56  // Calculate the next start position when DONT_CARE is specified.
57  start = (start == DONT_CARE) ? previous.start + previous.length : start;
58  }
59  // If start is DONT_CARE and does not inherit from the end of the previous task,
60  // which happens when registering the first task, set start to 0.
61  start = (start == DONT_CARE) ? 0 : start;
62  PhaserStateEntry& entry = m_state.entries[m_state.used];
63 
64  // Check assertions on the ports
65  FW_ASSERT(port < getNum_PhaserMemberOut_OutputPorts(), static_cast<FwAssertArgType>(port));
66  FW_ASSERT(isConnected_PhaserMemberOut_OutputPort(port), static_cast<FwAssertArgType>(port));
67  FW_ASSERT((start + length) <= m_cycle, static_cast<FwAssertArgType>(start), static_cast<FwAssertArgType>(length),
68  static_cast<FwAssertArgType>(m_cycle));
69  FW_ASSERT(context > m_cycle, static_cast<FwAssertArgType>(context), static_cast<FwAssertArgType>(m_cycle));
70 
71  entry.port = port;
72  entry.start = start;
73  entry.length = length;
74  // By default, context is DONT_CARE, which means the context type is SEQUENTIAL
75  // and a port's context value by default increments every time it is registered.
76  // If a value is given to context, the context type becomes COUNT, and
77  // entry.context represents the ratio between the user-configured context and the phaser cycle.
78  // The user-configured context must be greater than the phaser cycle.
79  // Example: If context == 2000 and m_cycle == 100, then entry.context == 20 while contextType == COUNT.
80  // FIXME: This is a point of confusion because entry.context and context are
81  // very different things, yet they have the same name.
82  entry.context = (context != DONT_CARE) ? context / m_cycle : getNextContext(port);
83  // Update some common multiple of all contexts
84  if (context != 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 = (context != 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  m_lock.lock();
114  U32 full_ticks = m_ticks;
115  m_lock.unLock();
116 
117  // If the cycle is over, wait for the cycle to end before restarting
118  if ((this->timeInCycle(full_ticks) >= m_cycle) && (m_state.current == m_state.used)) {
119  m_last_cycle_ticks = full_ticks;
120  // Increment cycle count modulo some common factor of all contexts
121  m_cycle_count = (m_cycle_count + 1) % m_ticks_rollover;
122  m_state.current = 0; // Back to processing the first task.
123  }
124  // Run the next child if the finishing child wast not late
125  if (finishChild(full_ticks) != ActivePhaser::FinishStatus::LATE) {
126  startChild(full_ticks);
127  }
128 }
129 
130 ActivePhaser::FinishStatus ActivePhaser ::finishChild(U32 full_ticks) {
131  // Guard against finishing improperly
132  if ((m_state.current >= m_state.used) || (not m_state.entries[m_state.current].started)) {
133  return ActivePhaser::FinishStatus::UNKNOWN;
134  }
135  // Only reachable here when current has not reached used
136  // and the current task was previously marked started.
137  // Now the task can be marked as done and the next task
138  // can be launched.
139  PhaserStateEntry& entry = m_state.entries[(m_state.current % m_state.used)];
140  const U32 execution_time = full_ticks - m_last_start_ticks;
141  const U32 expected_time = entry.length;
142 
143  // Mark entry as done
144  entry.started = false;
145  // Increment the current task index if it has not reached used, i.e., the max index registered.
146  m_state.current = (m_state.current == m_state.used) ? m_state.used : (m_state.current + 1);
147  // Check for overrun in timing. If a deadline violation is detected report this child as LATE
148  if (execution_time > expected_time) {
149  this->log_WARNING_HI_MissedDeadline(entry.port, entry.start, entry.length, (execution_time - expected_time));
150  return ActivePhaser::FinishStatus::LATE;
151  }
152  // If no overrun, proceed with the next child task.
153  return ActivePhaser::FinishStatus::ON_TIME;
154 }
155 
156 void ActivePhaser ::startChild(U32 full_ticks) {
157  // Guard against starting improperly
158  if ((m_state.current >= m_state.used) // Invalid. Current index surpasses the indices of registered tasks.
159  || (m_state.entries[m_state.current].start >
160  timeInCycle(full_ticks)) // Current time has not reached the intended start time.
161  || m_state.entries[m_state.current].started) // The current child task has already started.
162  {
163  return;
164  }
165  PhaserStateEntry& entry = m_state.entries[(m_state.current % m_state.used)];
166  // If context type is SEQUENTIAL, entry.context stores the number of times a port is called from the beginning of
167  // execution. If context type is COUNT, entry.context stores the number of phaser cycles elapsed within a
168  // user-specified time window.
169  U32 context = (entry.contextType == SEQUENTIAL) ? entry.context : m_cycle_count % entry.context;
170  entry.started = true;
171  m_last_start_ticks = full_ticks;
172  this->PhaserMemberOut_out(entry.port, context);
173 }
174 
175 U32 ActivePhaser ::getNextContext(FwIndexType port) {
176  U32 context = 0;
177  // Linear search to see if the entry's port matches the target port,
178  // if so, increment the context.
179  // Unlikely to overflow because this happens during registration.
180  for (U32 i = 0; i < m_state.used; i++) {
181  if (m_state.entries[i].port == port) {
182  context = m_state.entries[i].context + 1;
183  }
184  }
185  return context;
186 }
187 
188 U32 ActivePhaser ::timeInCycle(U32 full_ticks) {
189  return (full_ticks - m_last_cycle_ticks);
190 }
191 
192 } // 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
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)