diff options
Diffstat (limited to 'ucs2-lib/src/ucs_pmfifos.c')
-rw-r--r-- | ucs2-lib/src/ucs_pmfifos.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/ucs2-lib/src/ucs_pmfifos.c b/ucs2-lib/src/ucs_pmfifos.c new file mode 100644 index 0000000..347c1b9 --- /dev/null +++ b/ucs2-lib/src/ucs_pmfifos.c @@ -0,0 +1,448 @@ +/*------------------------------------------------------------------------------------------------*/ +/* UNICENS V2.1.0-3491 */ +/* Copyright (c) 2017 Microchip Technology Germany II GmbH & Co. KG. */ +/* */ +/* This program is free software: you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/* */ +/* You may also obtain this software under a propriety license from Microchip. */ +/* Please contact Microchip for further information. */ +/*------------------------------------------------------------------------------------------------*/ + +/*! + * \file + * \brief Implementation of class CPmFifos + * + * \cond UCS_INTERNAL_DOC + * \addtogroup G_PMFIFOS + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "ucs_pmfifos.h" +#include "ucs_misc.h" +#include "ucs_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Internal Constants */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief The initialization value of sync_count. It is incremented for each sync or un-sync attempt. */ +static const uint8_t FIFOS_SYNC_CNT_INITIAL = 0xFFU; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal typedefs */ +/*------------------------------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Fifos_Cleanup(CPmFifos *self); +static void Fifos_OnSyncTimeout(void *self); +static void Fifos_OnUnsyncTimeout(void *self); +static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr); + +static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id); +static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of class CPmFifos + * \param self The instance + * \param base_ptr Reference to basic services + * \param channel_ptr Reference to the port message channel + * \param icm_fifo_ptr Reference to ICM FIFO, or NULL. + * \param mcm_fifo_ptr Reference to MCM FIFO, or NULL. + * \param rcm_fifo_ptr Reference to RCM FIFO, or NULL. + * \details At least one FIFO (MCM or ICM) must be provided. + */ +void Fifos_Ctor(CPmFifos *self, CBase *base_ptr, CPmChannel *channel_ptr, CPmFifo *icm_fifo_ptr, CPmFifo *mcm_fifo_ptr, CPmFifo *rcm_fifo_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + + self->base_ptr = base_ptr; + self->channel_ptr = channel_ptr; + self->state = FIFOS_S_UNSYNCED; + + self->unsync_initial = false; + Fifos_ConfigureSyncParams(self, FIFOS_SYNC_RETRIES, FIFOS_SYNC_TIMEOUT); + + self->fifos[PMP_FIFO_ID_ICM] = icm_fifo_ptr; + self->fifos[PMP_FIFO_ID_RCM] = rcm_fifo_ptr; + self->fifos[PMP_FIFO_ID_MCM] = mcm_fifo_ptr; + + T_Ctor(&self->init_timer); + Sub_Ctor(&self->event_subject, self->base_ptr->ucs_user_ptr); + Obs_Ctor(&self->obs_icm, self, &Fifos_OnFifoEvent); + Obs_Ctor(&self->obs_rcm, self, &Fifos_OnFifoEvent); + Obs_Ctor(&self->obs_mcm, self, &Fifos_OnFifoEvent); + + TR_ASSERT(self->base_ptr->ucs_user_ptr, "[FIFOS]", (!((icm_fifo_ptr == NULL) && (mcm_fifo_ptr == NULL)))); + + if (icm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(icm_fifo_ptr, &self->obs_icm); + } + + if (rcm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(rcm_fifo_ptr, &self->obs_rcm); + } + + if (mcm_fifo_ptr != NULL) + { + Fifo_AddStateObserver(mcm_fifo_ptr, &self->obs_mcm); + } + + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Ctor(): FIFOS created, state %d", 1U, self->state)); +} + +/*! \brief Adds an observer of synchronization events + * \param self The instance + * \param obs_ptr The observer. The notification result type is Fifos_Event_t. + */ +void Fifos_AddEventObserver(CPmFifos *self, CObserver *obs_ptr) +{ + TR_ASSERT(self->base_ptr->ucs_user_ptr, "[FIFOS]", (obs_ptr != 0)); + (void)Sub_AddObserver(&self->event_subject, obs_ptr); +} + +/*! \brief Removes an observer of synchronization events + * \param self The instance + * \param obs_ptr The observer. + */ +void Fifos_RemoveEventObserver(CPmFifos *self, CObserver *obs_ptr) +{ + TR_ASSERT(self->base_ptr->ucs_user_ptr, "[FIFOS]", (obs_ptr != 0)); + (void)Sub_RemoveObserver(&self->event_subject, obs_ptr); +} + +/*! \brief Forces all FIFOs to state UNSYNCED without waiting for INIC responses and + * without throwing events + * \details Stops the LLD interface and releases all pending message resources. + * This function shall be called if the UCS requires a un-normal termination + * which is not detected by port message protocol. + * \param self The instance + */ +void Fifos_ForceTermination(CPmFifos *self) +{ + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_ForceTermination(): Termination started, state: %d", 1U, self->state)); + Fifos_Cleanup(self); + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_ForceTermination(): Termination done, state: %d", 1U, self->state)); +} + +/*! \brief Configures retries and timeout for synchronize or un-synchronize + * operation + * \details This method shall be called before starting a synchronization or un-synchronization + * or after it has finished. The current counter of synchronization attempts is reset. + * \param self The instance + * \param retries The number of retries until event FIFOS_EV_SYNC_FAILED or + * FIFOS_EV_UNSYNC_FAILED will be notified + * \param timeout The timeout in milliseconds when the retry is performed + */ +void Fifos_ConfigureSyncParams(CPmFifos *self, uint8_t retries, uint16_t timeout) +{ + self->cmd_retries = retries; + self->cmd_timeout = timeout; + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; +} + +/*------------------------------------------------------------------------------------------------*/ +/* Synchronization */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Initializes all port message FIFOs + * \details Possible results of the operation are the following events which are fired + * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t. + * - \ref FIFOS_EV_SYNC_ESTABLISHED + * - \ref FIFOS_EV_SYNC_FAILED + * \param self The instance + * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic + * retries will be done after the first synchronization timeout. + * \param force_sync If \c true the method will also trigger the synchronization of already + * synced \ref CPmFifo objects. + */ +void Fifos_Synchronize(CPmFifos *self, bool reset_cnt, bool force_sync) +{ + uint8_t cnt; + self->state = FIFOS_S_SYNCING; + self->unsync_initial = false; + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Synchronize(): Synchronization started, state: %d", 1U, self->state)); + + if (reset_cnt) + { + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; + } + + self->sync_cnt++; + Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */ + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + if (force_sync || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_SYNCED)) + { + Fifo_Synchronize(self->fifos[cnt]); + } + } + } + + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnSyncTimeout, self, + self->cmd_timeout, 0U); +} + +/*! \brief Un-initializes all port message FIFOs + * \details Possible results of the operation are the following events which are fired + * asynchronously. Refer also Fifos_AddEventObserver() and \ref Fifos_Event_t. + * - \ref FIFOS_EV_UNSYNC_COMPLETE + * - \ref FIFOS_EV_UNSYNC_FAILED + * \param self The instance + * \param reset_cnt If \c true resets the synchronization counter. In this case an automatic + * retries will be done after the first synchronization timeout. + * \param initial If the un-synchronization shall be executed prior to a initial synchronization + * it is recommended to set the argument to \c true. After notifying the event + * FIFOS_EV_UNSYNC_COMPLETE the LLD interface will not be stopped. The subsequent + * call of Fifos_Synchronize() will not start the LLD interface un-necessarily. + * To trigger a final un-synchronization \c initial shall be set to \c false. + * I.e., FIFOS_EV_UNSYNC_COMPLETE stops the LLD interface. + */ +void Fifos_Unsynchronize(CPmFifos *self, bool reset_cnt, bool initial) +{ + uint8_t cnt; + self->state = FIFOS_S_UNSYNCING; + self->unsync_initial = initial; + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Unsynchronize(): Un-synchronization started, state: %d", 1U, self->state)); + + if (reset_cnt) + { + self->sync_cnt = FIFOS_SYNC_CNT_INITIAL; + } + + self->sync_cnt++; + Pmch_Initialize(self->channel_ptr); /* Start LLD if not already done */ + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + if (initial || (Fifo_GetState(self->fifos[cnt]) != FIFO_S_UNSYNCED_READY)) + { + Fifo_Unsynchronize(self->fifos[cnt]); + } + } + } + + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnUnsyncTimeout, self, + self->cmd_timeout, 0U); +} + +/*! \brief Handles the synchronization timeout + * \param self The instance + */ +static void Fifos_OnSyncTimeout(void *self) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_Event_t the_event = FIFOS_EV_SYNC_FAILED; + + self_->state = FIFOS_S_UNSYNCED; + + TR_INFO((self_->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_OnSyncTimeout(): state: %d", 1U, self_->state)); + + if (self_->sync_cnt < self_->cmd_retries) + { + Fifos_Synchronize(self_, false, false); /* retry synchronization after first timeout */ + } + else + { + Fifos_Cleanup(self_); + Sub_Notify(&self_->event_subject, &the_event); + } +} + +/*! \brief Handles the un-synchronization timeout + * \param self The instance + */ +static void Fifos_OnUnsyncTimeout(void *self) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_Event_t the_event = FIFOS_EV_UNSYNC_FAILED; + + self_->state = FIFOS_S_UNSYNCED; + TR_INFO((self_->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_OnUnsyncTimeout(): state: %d", 1U, self_->state)); + + if (self_->sync_cnt < self_->cmd_retries) + { + Fifos_Unsynchronize(self_, false, self_->unsync_initial); /* retry synchronization after first timeout */ + } + else + { + self_->unsync_initial = false; /* un-sync timeout will lead to termination - stop LLD */ + Fifos_Cleanup(self_); + Sub_Notify(&self_->event_subject, &the_event); + } +} + +/*! \brief Performs a cleanup of the Port Message Channel and the dedicated FIFOs + * \details Releases all message objects which are currently in use. + * \param self The instance + */ +static void Fifos_Cleanup(CPmFifos *self) +{ + uint8_t count; + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup started", 0U)); + + if (self->unsync_initial == false) + { + Pmch_Uninitialize(self->channel_ptr); + } + + for (count = 0U; count < PMP_MAX_NUM_FIFOS; count++) /* stop & cleanup all FIFOs */ + { + if (self->fifos[count] != NULL) + { /* stop and avoid recursion */ + Fifo_Stop(self->fifos[count], FIFO_S_UNSYNCED_INIT, false); + Fifo_Cleanup(self->fifos[count]); + } + } + + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_Cleanup(): Channel cleanup completed", 0U)); + + /* notify external event after message objects were released */ + self->state = FIFOS_S_UNSYNCED; +} + +/*------------------------------------------------------------------------------------------------*/ +/* FIFO observation */ +/*------------------------------------------------------------------------------------------------*/ + +/*! \brief Notifies an event to the host class + * \param self The instance + * \param fifo_id_ptr Specific event identifier, pointer to "fifo_id" + */ +static void Fifos_OnFifoEvent(void *self, void *fifo_id_ptr) +{ + CPmFifos *self_ = (CPmFifos*)self; + Fifos_HandleFifoStateChange(self_, *((Pmp_FifoId_t*)fifo_id_ptr)); +} + +/*! \brief Executes transition to new synchronization states + * \param self The instance + * \param fifo_id The FIFO identifier + */ +static void Fifos_HandleFifoStateChange(CPmFifos *self, Pmp_FifoId_t fifo_id) +{ + Fifos_Event_t the_event; + + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): FIFOs state: %d, FIFO: %d, FIFO State: %d", 3U, + self->state, fifo_id, Fifo_GetState(self->fifos[fifo_id]))); + + switch (self->state) + { + case FIFOS_S_SYNCING: + if (Fifos_AreAllFifosInState(self, FIFO_S_SYNCED)) + { + self->state = FIFOS_S_SYNCED; /* now the complete channel is synced */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + Fifos_ConfigureSyncParams(self, FIFOS_UNSYNC_RETRIES, FIFOS_UNSYNC_TIMEOUT); + the_event = FIFOS_EV_SYNC_ESTABLISHED; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_UNSYNCING: + if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY)) + { + Fifos_Cleanup(self); + self->state = FIFOS_S_UNSYNCED; /* now the complete channel is un-synced */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + the_event = FIFOS_EV_UNSYNC_COMPLETE; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Un-synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_SYNCED: + if (!Fifos_AreAllFifosInState(self, FIFO_S_SYNCED)) + { + self->state = FIFOS_S_UNSYNCING; /* set state to 'unsyncing' and wait until all FIFOs are unsynced */ + self->sync_cnt = 0U; /* pretend having triggered an un-sync which starts the timer */ + Tm_SetTimer(&self->base_ptr->tm, &self->init_timer, + &Fifos_OnUnsyncTimeout, self, + FIFOS_UNSYNC_TIMEOUT, 0U); + the_event = FIFOS_EV_SYNC_LOST; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Lost synchronization of Port Message channel", 0U)); + } + if (Fifos_AreAllFifosInState(self, FIFO_S_UNSYNCED_READY)) + { + Fifos_Cleanup(self); + self->state = FIFOS_S_UNSYNCED; /* the complete channel suddenly goes unsynced_complete */ + Tm_ClearTimer(&self->base_ptr->tm, &self->init_timer); + the_event = FIFOS_EV_UNSYNC_COMPLETE; + Sub_Notify(&self->event_subject, &the_event); + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Sudden un-synchronization of Port Message channel completed", 0U)); + } + break; + + case FIFOS_S_UNSYNCED: + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFO event in state unsynced", 0U)); + break; + + default: + TR_INFO((self->base_ptr->ucs_user_ptr, "[FIFOS]", "Fifos_HandleFifoStateChange(): Unexpected FIFOs state", 0U)); + break; + } + + MISC_UNUSED(fifo_id); +} + +/*! \brief Helper function that evaluates if all configured FIFOs are in a given state + * \param self The instance + * \param target_state The required state that is evaluated for all FIFOs + * \return \c true if all FIFOs are in the given \c target_state, otherwise \c false. + */ +static bool Fifos_AreAllFifosInState(CPmFifos *self, Fifo_SyncState_t target_state) +{ + bool ret = true; + uint8_t cnt; + + for (cnt = 0U; cnt < PMP_MAX_NUM_FIFOS; cnt++) + { + if (self->fifos[cnt] != NULL) + { + Fifo_SyncState_t state = Fifo_GetState(self->fifos[cnt]); + + if (state != target_state) + { + ret = false; + } + } + } + + return ret; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + |