1 /*------------------------------------------------------------------------------------------------*/
2 /* UNICENS V2.1.0-3491 */
3 /* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */
5 /* This program is free software: you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation, either version 2 of the License, or */
8 /* (at your option) any later version. */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU General Public License for more details. */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* You may also obtain this software under a propriety license from Microchip. */
19 /* Please contact Microchip for further information. */
20 /*------------------------------------------------------------------------------------------------*/
24 * \brief Implementation of class CPmFifos
26 * \cond UCS_INTERNAL_DOC
27 * \addtogroup G_PMFIFOS
31 /*------------------------------------------------------------------------------------------------*/
33 /*------------------------------------------------------------------------------------------------*/
34 #include "ucs_pmfifos.h"
36 #include "ucs_trace.h"
38 /*------------------------------------------------------------------------------------------------*/
39 /* Internal Constants */
40 /*------------------------------------------------------------------------------------------------*/
41 /*! \brief The initialization value of sync_count. It is incremented for each sync or un-sync attempt. */
42 static const uint8_t FIFOS_SYNC_CNT_INITIAL = 0xFFU;
44 /*------------------------------------------------------------------------------------------------*/
45 /* Internal typedefs */
46 /*------------------------------------------------------------------------------------------------*/
48 /*------------------------------------------------------------------------------------------------*/
49 /* Internal prototypes */
50 /*------------------------------------------------------------------------------------------------*/
51 static void Fifos_Cleanup(CPmFifos *self);
52 static void Fifos_OnSyncTimeout(void *self);
53 static void Fifos_OnUnsyncTimeout(void *self);
54 static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr);
56 static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id);
57 static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state);
59 /*------------------------------------------------------------------------------------------------*/
61 /*------------------------------------------------------------------------------------------------*/
62 /*! \brief Constructor of class CPmFifos
63 * \param self The instance
64 * \param base_ptr Reference to basic services
65 * \param channel_ptr Reference to the port message channel
66 * \param icm_fifo_ptr Reference to ICM FIFO, or NULL.
67 * \param mcm_fifo_ptr Reference to MCM FIFO, or NULL.
68 * \param rcm_fifo_ptr Reference to RCM FIFO, or NULL.
69 * \details At least one FIFO (MCM or ICM) must be provided.
71 void Fifos_Ctor(CPmFifos *self, CBase *base_ptr, CPmChannel *channel_ptr, CPmFifo *icm_fifo_ptr, CPmFifo *mcm_fifo_ptr, CPmFifo *rcm_fifo_ptr)
73 MISC_MEM_SET(self, 0, sizeof(*self));
75 self->base_ptr = base_ptr;
76 self->channel_ptr = channel_ptr;
77 self->state = FIFOS_S_UNSYNCED;
79 self->unsync_initial = false;
80 Fifos_ConfigureSyncParams(self, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT);
82 self->fifos[PMP_FIFO_ID_ICM] = icm_fifo_ptr;
83 self->fifos[PMP_FIFO_ID_RCM] = rcm_fifo_ptr;
84 self->fifos[PMP_FIFO_ID_MCM] = mcm_fifo_ptr;
86 T_Ctor(&self->init_timer);
87 Sub_Ctor(&self->event_subject, self->base_ptr->ucs_user_ptr);
88 Obs_Ctor(&self->obs_icm, self, &Fifos_OnFifoEvent);
89 Obs_Ctor(&self->obs_rcm, self, &Fifos_OnFifoEvent);
90 Obs_Ctor(&self->obs_mcm, self, &Fifos_OnFifoEvent);
92 TR_ASSERT(self->base_ptr->ucs_user_ptr, "[FIFOS]", (!((icm_fifo_ptr == NULL) && (mcm_fifo_ptr == NULL))));
94 if (icm_fifo_ptr != NULL)
96 Fifo_AddStateObserver(icm_fifo_ptr, &self->obs_icm);
99 if (rcm_fifo_ptr != NULL)
101 Fifo_AddStateObserver(rcm_fifo_ptr, &self->obs_rcm);
104 if (mcm_fifo_ptr != NULL)
106 Fifo_AddStateObserver(mcm_fifo_ptr, &self->obs_mcm);
109 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Ctor(): FIFOS created, state %d", 1U, self->state));
112 /*! \brief Adds an observer of synchronization events
113 * \param self The instance
114 * \param obs_ptr The observer. The notification result type is Fifos_Event_t.
116 void Fifos_AddEventObserver(CPmFifos *self, CObserver *obs_ptr)
118 TR_ASSERT(self->base_ptr->ucs_user_ptr, "[FIFOS]", (obs_ptr != 0));
119 (void)Sub_AddObserver(&self->event_subject, obs_ptr);
122 /*! \brief Removes an observer of synchronization events
123 * \param self The instance
124 * \param obs_ptr The observer.
126 void Fifos_RemoveEventObserver(CPmFifos *self, CObserver *obs_ptr)
128 TR_ASSERT(self->base_ptr->ucs_user_ptr, "[FIFOS]", (obs_ptr != 0));
129 (void)Sub_RemoveObserver(&self->event_subject, obs_ptr);
132 /*! \brief Forces all FIFOs to state UNSYNCED without waiting for INIC responses and
133 * without throwing events
134 * \details Stops the LLD interface and releases all pending message resources.
135 * This function shall be called if the UCS requires a un-normal termination
136 * which is not detected by port message protocol.
137 * \param self The instance
139 void Fifos_ForceTermination(CPmFifos *self)
141 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_ForceTermination(): Termination started, state: %d", 1U, self->state));
143 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_ForceTermination(): Termination done, state: %d", 1U, self->state));
146 /*! \brief Configures retries and timeout for synchronize or un-synchronize
148 * \details This method shall be called before starting a synchronization or un-synchronization
149 * or after it has finished. The current counter of synchronization attempts is reset.
150 * \param self The instance
151 * \param retries The number of retries until event FIFOS_EV_SYNC_FAILED or
152 * FIFOS_EV_UNSYNC_FAILED will be notified
153 * \param timeout The timeout in milliseconds when the retry is performed
155 void Fifos_ConfigureSyncParams(CPmFifos *self, uint8_t retries, uint16_t timeout)
157 self->cmd_retries = retries;
158 self->cmd_timeout = timeout;
159 self->sync_cnt = FIFOS_SYNC_CNT_INITIAL;
162 /*------------------------------------------------------------------------------------------------*/
163 /* Synchronization */
164 /*------------------------------------------------------------------------------------------------*/
165 /*! \brief Initializes all port message FIFOs
166 * \details Possible results of the operation are the following events which are fired
167 * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t.
168 * - \ref FIFOS_EV_SYNC_ESTABLISHED
169 * - \ref FIFOS_EV_SYNC_FAILED
170 * \param self The instance
171 * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic
172 * retries will be done after the first synchronization timeout.
173 * \param force_sync If \c true the method will also trigger the synchronization of already
174 * synced \ref CPmFifo objects.
176 void Fifos_Synchronize(CPmFifos *self, bool reset_cnt, bool force_sync)
179 self->state = FIFOS_S_SYNCING;
180 self->unsync_initial = false;
181 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Synchronize(): Synchronization started, state: %d", 1U, self->state));
185 self->sync_cnt = FIFOS_SYNC_CNT_INITIAL;
189 Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */
191 for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++)
193 if (self->fifos[cnt] != NULL)
195 if (force_sync || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_SYNCED))
197 Fifo_Synchronize(self->fifos[cnt]);
202 Tm_SetTimer(&self->base_ptr->tm, &self->init_timer,
203 &Fifos_OnSyncTimeout, self,
204 self->cmd_timeout, 0U);
207 /*! \brief Un-initializes all port message FIFOs
208 * \details Possible results of the operation are the following events which are fired
209 * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t.
210 * - \ref FIFOS_EV_UNSYNC_COMPLETE
211 * - \ref FIFOS_EV_UNSYNC_FAILED
212 * \param self The instance
213 * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic
214 * retries will be done after the first synchronization timeout.
215 * \param initial If the un-synchronization shall be executed prior to a initial synchronization
216 * it is recommended to set the argument to \c true. After notifying the event
217 * FIFOS_EV_UNSYNC_COMPLETE the LLD interface will not be stopped. The subsequent
218 * call of Fifos_Synchronize() will not start the LLD interface un-necessarily.
219 * To trigger a final un-synchronization \c initial shall be set to \c false.
220 * I.e., FIFOS_EV_UNSYNC_COMPLETE stops the LLD interface.
222 void Fifos_Unsynchronize(CPmFifos *self, bool reset_cnt, bool initial)
225 self->state = FIFOS_S_UNSYNCING;
226 self->unsync_initial = initial;
227 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Unsynchronize(): Un-synchronization started, state: %d", 1U, self->state));
231 self->sync_cnt = FIFOS_SYNC_CNT_INITIAL;
235 Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */
237 for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++)
239 if (self->fifos[cnt] != NULL)
241 if (initial || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_UNSYNCED_READY))
243 Fifo_Unsynchronize(self->fifos[cnt]);
248 Tm_SetTimer(&self->base_ptr->tm, &self->init_timer,
249 &Fifos_OnUnsyncTimeout, self,
250 self->cmd_timeout, 0U);
253 /*! \brief Handles the synchronization timeout
254 * \param self The instance
256 static void Fifos_OnSyncTimeout(void *self)
258 CPmFifos *self_ = (CPmFifos*)self;
259 Fifos_Event_t the_event = FIFOS_EV_SYNC_FAILED;
261 self_->state = FIFOS_S_UNSYNCED;
263 TR_INFO((self_->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_OnSyncTimeout(): state: %d", 1U, self_->state));
265 if (self_->sync_cnt < self_->cmd_retries)
267 Fifos_Synchronize(self_, false, false); /* retry synchronization after first timeout */
271 Fifos_Cleanup(self_);
272 Sub_Notify(&self_->event_subject, &the_event);
276 /*! \brief Handles the un-synchronization timeout
277 * \param self The instance
279 static void Fifos_OnUnsyncTimeout(void *self)
281 CPmFifos *self_ = (CPmFifos*)self;
282 Fifos_Event_t the_event = FIFOS_EV_UNSYNC_FAILED;
284 self_->state = FIFOS_S_UNSYNCED;
285 TR_INFO((self_->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_OnUnsyncTimeout(): state: %d", 1U, self_->state));
287 if (self_->sync_cnt < self_->cmd_retries)
289 Fifos_Unsynchronize(self_, false, self_->unsync_initial); /* retry synchronization after first timeout */
293 self_->unsync_initial = false; /* un-sync timeout will lead to termination - stop LLD */
294 Fifos_Cleanup(self_);
295 Sub_Notify(&self_->event_subject, &the_event);
299 /*! \brief Performs a cleanup of the Port Message Channel and the dedicated FIFOs
300 * \details Releases all message objects which are currently in use.
301 * \param self The instance
303 static void Fifos_Cleanup(CPmFifos *self)
306 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup started", 0U));
308 if (self->unsync_initial == false)
310 Pmch_Uninitialize(self->channel_ptr);
313 for (count = 0U; count < PMP_MAX_NUM_FIFOS; count++) /* stop & cleanup all FIFOs */
315 if (self->fifos[count] != NULL)
316 { /* stop and avoid recursion */
317 Fifo_Stop(self->fifos[count], FIFO_S_UNSYNCED_INIT, false);
318 Fifo_Cleanup(self->fifos[count]);
322 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup completed", 0U));
324 /* notify external event after message objects were released */
325 self->state = FIFOS_S_UNSYNCED;
328 /*------------------------------------------------------------------------------------------------*/
329 /* FIFO observation */
330 /*------------------------------------------------------------------------------------------------*/
332 /*! \brief Notifies an event to the host class
333 * \param self The instance
334 * \param fifo_id_ptr Specific event identifier, pointer to "fifo_id"
336 static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr)
338 CPmFifos *self_ = (CPmFifos*)self;
339 Fifos_HandleFifoStateChange(self_, *((Pmp_FifoId_t*)fifo_id_ptr));
342 /*! \brief Executes transition to new synchronization states
343 * \param self The instance
344 * \param fifo_id The FIFO identifier
346 static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id)
348 Fifos_Event_t the_event;
350 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): FIFOs state: %d, FIFO: %d, FIFO State: %d", 3U,
351 self->state, fifo_id, Fifo_GetState(self->fifos[fifo_id])));
355 case FIFOS_S_SYNCING:
356 if (Fifos_AreAllFifosInState(self, FIFO_S_SYNCED))
358 self->state = FIFOS_S_SYNCED; /* now the complete channel is synced */
359 Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer);
360 Fifos_ConfigureSyncParams(self, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT);
361 the_event = FIFOS_EV_SYNC_ESTABLISHED;
362 Sub_Notify(&self->event_subject, &the_event);
363 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Synchronization of Port Message channel completed", 0U));
367 case FIFOS_S_UNSYNCING:
368 if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY))
371 self->state = FIFOS_S_UNSYNCED; /* now the complete channel is un-synced */
372 Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer);
373 the_event = FIFOS_EV_UNSYNC_COMPLETE;
374 Sub_Notify(&self->event_subject, &the_event);
375 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Un-synchronization of Port Message channel completed", 0U));
380 if (!Fifos_AreAllFifosInState(self, FIFO_S_SYNCED))
382 self->state = FIFOS_S_UNSYNCING; /* set state to 'unsyncing' and wait until all FIFOs are unsynced */
383 self->sync_cnt = 0U; /* pretend having triggered an un-sync which starts the timer */
384 Tm_SetTimer(&self->base_ptr->tm, &self->init_timer,
385 &Fifos_OnUnsyncTimeout, self,
386 FIFOS_UNSYNC_TIMEOUT, 0U);
387 the_event = FIFOS_EV_SYNC_LOST;
388 Sub_Notify(&self->event_subject, &the_event);
389 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Lost synchronization of Port Message channel", 0U));
391 if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY))
394 self->state = FIFOS_S_UNSYNCED; /* the complete channel suddenly goes unsynced_complete */
395 Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer);
396 the_event = FIFOS_EV_UNSYNC_COMPLETE;
397 Sub_Notify(&self->event_subject, &the_event);
398 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Sudden un-synchronization of Port Message channel completed", 0U));
402 case FIFOS_S_UNSYNCED:
403 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFO event in state unsynced", 0U));
407 TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFOs state", 0U));
411 MISC_UNUSED(fifo_id);
414 /*! \brief Helper function that evaluates if all configured FIFOs are in a given state
415 * \param self The instance
416 * \param target_state The required state that is evaluated for all FIFOs
417 * \return \c true if all FIFOs are in the given \c target_state, otherwise \c false.
419 static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state)
424 for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++)
426 if (self->fifos[cnt] != NULL)
428 Fifo_SyncState_t state = Fifo_GetState(self->fifos[cnt]);
430 if (state != target_state)
445 /*------------------------------------------------------------------------------------------------*/
447 /*------------------------------------------------------------------------------------------------*/