/**
 * @file		hal_system.c
 * @copyright  	Nehemis SARL reserves all rights even in the event of industrial
 *             	property rights. We reserve all rights of disposal such as
 *             	copying and passing on to third parties.
 *
 *
 * @brief       HAL layer for the SYSTEM module.
 *              - The system reset cause such as :
 *                  1. Software reset
 *                  2. Watchdog reset
 *                  3. Power on reset
 *                  4. Optional byte load reset
 *                  5. Low power reset
 *                  6. Pin reset
 *              - The NVIC enable/disable interrupts.
 *              - The serial number of the MCU.
 */

/******************************************************************************
 * Include Header Files
 ******************************************************************************/
#include "hal_system.h"
#include "string.h"

/* cubeMX */
#include "main.h"
/******************************************************************************
 * Macro Constant Declarations
 ******************************************************************************/
#define HAL_SYSTEM_STACK_GUARD      0xCDu

/******************************************************************************
 * Module Global Variable Declarations
 ******************************************************************************/
static uint64 systemStepCounter_gu64;
static uint64 systemPeriodMsToUs_gu64;
static uint64 systemTickLoadValue_gu64;
static uint32 resetStatusRegister_gu32;

/******************************************************************************
 * Static Function Declarations
 ******************************************************************************/

/******************************************************************************
 * Extern Function Definitions
 ******************************************************************************/
uint32 HalSystemInit(void)
{
    uint32 error_u32 = NMS_ERR_NONE;

    resetStatusRegister_gu32 = RCC->CSR;
    RCC->CSR |= RCC_CSR_RMVF; /* RMVF = ReMoVe Flag. Reset the reset cause register for the next reboot. */

    systemPeriodMsToUs_gu64 = HAL_GetTickFreq() * 1000uLL;
    systemTickLoadValue_gu64 = (uint64)(SysTick->LOAD);
    systemStepCounter_gu64 = 0uLL;

    return error_u32;
}


uint32 HalSystemGetRunTimeUs(uint64 * resultUs_pu64)
{
    static uint64 previousTimeUs_u64 = 0uLL;
    uint32 error_u32 = NMS_ERR_NONE;
    uint64 timeUs_u64 = 0uLL;

    /* Make a copy in case of multiple function instance at the same time */
    uint64 copySystemStepCounter_u64 = systemStepCounter_gu64;

    /* First make a copy of the systemStepCounter to be able to check rollback further on.
     * Also copy current value of the System Tick Timer to avoid any dynamic update during calculation.
     * Then translate in microsecond & sum both timing information (step counter + tick value) to get the most
     * accurate resolution of the system runtime.
     */
    uint64 copyPreviousTimeUs_u64 = previousTimeUs_u64;
    uint64 tickValue_u64 = (uint64)(SysTick->VAL);
    timeUs_u64 = copySystemStepCounter_u64 * systemPeriodMsToUs_gu64;
    timeUs_u64 += ((systemTickLoadValue_gu64 - tickValue_u64) * systemPeriodMsToUs_gu64) / systemTickLoadValue_gu64;

    /* We check here if the interruption of the systick happened during our operations and correct it accordingly */
    if(timeUs_u64 <= (uint64)(systemStepCounter_gu64 * systemPeriodMsToUs_gu64))
    {
        copySystemStepCounter_u64 = systemStepCounter_gu64;
        tickValue_u64 = (uint64)(SysTick->VAL);
        timeUs_u64 = copySystemStepCounter_u64 * systemPeriodMsToUs_gu64;
        timeUs_u64 += ((systemTickLoadValue_gu64 - tickValue_u64) * systemPeriodMsToUs_gu64) / systemTickLoadValue_gu64;

        /* The timer interruption is too fast, no time to process */
        if(timeUs_u64 <= (uint64)(systemStepCounter_gu64 * systemPeriodMsToUs_gu64))
        {
            error_u32 =  NMS_ERR_OVERRUN;
        }
    }

    /* Should never happen if this timer interruption is set to highest priority */
    if(copyPreviousTimeUs_u64 > timeUs_u64)
    {
        error_u32 = NMS_ERR_UNKNOWN;
    }
    else
    {
        *resultUs_pu64 = previousTimeUs_u64;
    }

    /* This condition has been added to prevent trouble overflow with interruptions
       on the medium and high optimizations options */
    *resultUs_pu64 = (timeUs_u64 > previousTimeUs_u64) ? timeUs_u64 : *resultUs_pu64;

    return error_u32;
}


/* This function should not call the log, since it uses the system runtime. */
uint32 HalSystemGetRunTimeMs(uint64 * resultMs_pu64)
{
    *resultMs_pu64 = systemStepCounter_gu64;

    return NMS_ERR_NONE;
}


void HalSystemGetSerialId(uint8 * serialIdBuffer_pu8)
{
    uint32 serialIdW0_u32, serialIdW1_u32, serialIdW2_u32;

    /* Get the three parts of the UID */
    serialIdW0_u32 = HAL_GetUIDw0();
    serialIdW1_u32 = HAL_GetUIDw1();
    serialIdW2_u32 = HAL_GetUIDw2();

    /* Copy the values into the serial ID buffer */
    memcpy(&serialIdBuffer_pu8[0], &serialIdW0_u32, sizeof(serialIdW0_u32));
    memcpy(&serialIdBuffer_pu8[4], &serialIdW1_u32, sizeof(serialIdW1_u32));
    memcpy(&serialIdBuffer_pu8[8], &serialIdW2_u32, sizeof(serialIdW2_u32));
}


uint32 HalSystemGetResetCause( HalSystemResetCauseEn resetCauseSelector_en, uint32 * causes_pu32 )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    uint32 mask_u32  = 0uL;

    if (causes_pu32 == NULL)
    {
        error_u32 = NMS_ERR_INVALID_POINTER;
    }
    else
    {
        switch(resetCauseSelector_en)
        {
			case HAL_SYSTEM_RESET_CAUSE_OPTION_BYTE_LOADER:
				mask_u32 = RCC_CSR_OBLRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_EXTERNAL_PIN_RESET:
				mask_u32 = RCC_CSR_PINRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_BROWNOUT_RESET:
				mask_u32 = RCC_CSR_BORRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_SOFTWARE_RESET:
				mask_u32 = RCC_CSR_SFTRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_WATCH_DOG_TIMER:
				mask_u32 = RCC_CSR_IWDGRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_WINDOW_WATCH_DOG:
				mask_u32 = RCC_CSR_WWDGRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_LOW_POWER:
				mask_u32 = RCC_CSR_LPWRRSTF_Msk;
				error_u32 = NMS_ERR_NONE;
				break;

			case HAL_SYSTEM_RESET_CAUSE_ALL_CAUSES:
				mask_u32 = RCC_CSR_IWDGRSTF_Msk
						 | RCC_CSR_WWDGRSTF_Msk
						 | RCC_CSR_SFTRSTF_Msk
						 | RCC_CSR_PINRSTF_Msk
						 | RCC_CSR_LPWRRSTF_Msk
						 | RCC_CSR_BORRSTF_Msk;
				error_u32 =  NMS_ERR_NONE;
				break;

			default:

				error_u32 = NMS_ERR_UNKNOWN;
				break;
        }

        *causes_pu32 = resetStatusRegister_gu32 & mask_u32;
    }

    return error_u32;
}


void HalSystemDisableInterrupts(void)
{
    __disable_irq();
}


void HalSystemEnableInterrupts(void)
{
    __enable_irq();
}


void HalSystemReset(void)
{
    HAL_NVIC_SystemReset();
}

/******************************************************************************
 * Callback Function Definitions
 ******************************************************************************/
/**
* @brief    Count the number of milliseconds that elapsed since the start
*
* @note     Function called by low-level functions of cubeMX
*/
void HAL_IncTick(void)
{
    systemStepCounter_gu64++;
}


/**
* @brief    Provides a tick value in millisecond.
*
* @return   The number of elapsed tick.
*/
uint32 HAL_GetTick(void)
{
  return (uint32)systemStepCounter_gu64;
}
