/*
 * Copyright 2020-2022 Yuntu Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef CORDIC_DRIVER_H
#define CORDIC_DRIVER_H

/*! @file cordic_driver.h */

#include <stddef.h>
#include "device_registers.h"
#include "status.h"

/*!
 * @defgroup cordic_driver CORDIC Driver
 * @ingroup cordic
 * @brief Coordinate Rotation Digital Computer Peripheral Driver
 * @details This section describes the programming interface of the CORDIC driver.
 * @{
 */
/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*!
 * @brief CORDIC algorithm enumeration
 * Implements : cordic_algorithm_t_Class
 */
typedef enum
{
    CORDIC_ALG_COS,             /*!< outA = cos(z) */
    CORDIC_ALG_SIN,             /*!< outB = sin(z) */        
    CORDIC_ALG_MOD,             /*!< outA = sqrt(x^2 + y^2) */
    CORDIC_ALG_ARCTAN,          /*!< outB = arctan(y/x) */
    CORDIC_ALG_MULTIPLY,        /*!< outB = x * z */
    CORDIC_ALG_DIVIDER,         /*!< outB = y * x */
    CORDIC_ALG_COSH,            /*!< outA = cosh(z) */
    CORDIC_ALG_SINH,            /*!< outB = sinh(z) */
    CORDIC_ALG_DIFF,            /*!< outA = sqrt(x^2 - y^2) */
    CORDIC_ALG_ARCTANH,         /*!< outB = arctanh(y/x) */
    CORDIC_ALG_EXP,             /*!< outA = e^z */
} cordic_algorithm_t;

/*!
 * @brief CORDIC precision enumeration
 * Implements : cordic_precision_t_Class
 */
typedef enum
{
    CORDIC_PRECISION_LOW = 1,       /*!< Cordic use low precision */
    CORDIC_PRECISION_MEDIUM = 0,    /*!< Cordic use medium precision */
    CORDIC_PRECISION_HIGH = 2,      /*!< Cordic use high precision */
} cordic_precision_t;

/*!
 * @brief CORDIC input and output format
 * Implements : cordic_format_t_Class
 */
typedef enum
{
    CORDIC_Q1P15 = 1,       /*!< Cordic input and output format is q1.15 */
    CORDIC_Q1P31 = 0        /*!< Cordic input and output format is q1.31 */
} cordic_format_t;

/*!
 * @brief CORDIC result scale
 * Implements : cordic_scale_t_Class
 */
typedef enum
{
    CORDIC_SCALE_x1 = 1,       /*!< Cordic result * 1 */
    CORDIC_SCALE_x2 = 2,       /*!< Cordic result * 2 */
    CORDIC_SCALE_x4 = 4,       /*!< Cordic result * 4 */
} cordic_scale_t;

/*!
 * @brief CORDIC configuration structure.
 * Implements : cordic_user_config_t_Class
 */
typedef struct
{
    bool errorTolerance;            /*!< Decide if abort cordic calculation when error detected */
    bool errorInterrupt;            /*!< Enable error detect interrupt */
    cordic_algorithm_t algorithm;   /*!< Algorithm select for CORDIC */
    cordic_precision_t precision;   /*!< Precision select for CORDIC */
    cordic_format_t format;         /*!< Input and output format for CORDIC */
} cordic_user_config_t;

/*******************************************************************************
 * API
 ******************************************************************************/
/*!
 * @name CORDIC DRIVER API
 * @{
 */

#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief Initializes the CORDIC module
 *
 * This function initializes cordic driver based on user configuration input.
 * The user must make sure that the clock is enabled
 *
 * @param[in] instance The cordic instance number
 * @param[in] userConfig Pointer to structure of initialization
 */
status_t CORDIC_DRV_Init(uint8_t instance, const cordic_user_config_t *userConfig);

/*!
 * @brief Sets the default configuration
 *
 * This function sets the default configuration
 *
 * @param[in] instance The cordic instance number
 */
status_t CORDIC_DRV_Deinit(uint8_t instance);

/*!
 * @brief Clear error status for cordic
 *
 * This function clear input error status for cordic.
 *
 * @param[in] instance The cordic instance number
 */
void CORDIC_DRV_ClearErrorStatus(uint8_t instance);

/*!
 * @brief Check CORDIC if busy
 *
 * This function check CORDIC if busy.
 *
 * @param[in] base CORDIC peripheral address
 */
static inline bool CORDIC_IsBusy(CORDIC_Type *base)
{
    return ((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
}

/*!
 * @brief Get cos result
 *
 * This function get cos(z) result by CORDIC.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] z angle for cos calculation
 */
static inline uint32_t CORDIC_GetCos(CORDIC_Type *base, uint32_t z)
{
    base->Z = z;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    return base->OUTA;
}

/*!
 * @brief Get sin result
 *
 * This function get sin(z) result by CORDIC.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] z angle for sin calculation
 */
static inline uint32_t CORDIC_GetSin(CORDIC_Type *base, uint32_t z)
{
    base->Z = z;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    return base->OUTB;
}

/*!
 * @brief Get mod result
 *
 * This function get sqrt(x^2 + y^2) result by CORDIC.
 * The true result need multiply to scale.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] x The first param for MOD calculation
 * @param[in] y The second param for MOD calculation
 * @param[out] scale The scale for result need multiply
 */
static inline uint32_t CORDIC_GetMod(CORDIC_Type *base, uint32_t x, uint32_t y, uint8_t *scale)
{
    base->X = x;
    base->Y = y;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    *scale = (base->STS & CORDIC_STS_SCALE_MASK) >> CORDIC_STS_SCALE_SHIFT;
    return base->OUTA;
}

/*!
 * @brief Get arctan result
 *
 * This function get arctan(y/x) result by CORDIC.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] x The divisor for arctan calculation
 * @param[in] y The dividend for arctan calculation
 */
static inline uint32_t CORDIC_GetArcTan(CORDIC_Type *base, uint32_t x, uint32_t y)
{
    base->X = x;
    base->Y = y;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    return base->OUTB;
}

/*!
 * @brief Get multiply result
 *
 * This function get x*y result by CORDIC.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] x The first param for multiply calculation
 * @param[in] y The second param for multiply calculation
 */
static inline uint32_t CORDIC_Multiply(CORDIC_Type *base, uint32_t x, uint32_t z)
{
    base->X = x;
    base->Z = z;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    return base->OUTB;
}

/*!
 * @brief Get divider result
 *
 * This function get y/x result by CORDIC.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] x The divisor for divider calculation
 * @param[in] y The dividend for divider calculation
 */
static inline uint32_t CORDIC_Divider(CORDIC_Type *base, uint32_t x, uint32_t y)
{
    base->X = x;
    base->Y = y;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    return base->OUTB;
}

/*!
 * @brief Get cosh result
 *
 * This function get cosh(z) result by CORDIC.
 * The true result need multiply to scale.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] z param for cosh calculation
 * @param[out] scale The scale for result need multiply
 */
static inline uint32_t CORDIC_GetCosh(CORDIC_Type *base, uint32_t z, uint8_t *scale)
{
    base->Z = z;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    *scale = (base->STS & CORDIC_STS_SCALE_MASK) >> CORDIC_STS_SCALE_SHIFT;
    return base->OUTA;
}

/*!
 * @brief Get sinh result
 *
 * This function get sinh(z) result by CORDIC.
 * The true result need multiply to scale.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] z param for sinh calculation
 * @param[out] scale The scale for result need multiply
 */
static inline uint32_t CORDIC_GetSinh(CORDIC_Type *base, uint32_t z, uint8_t *scale)
{
    base->Z = z;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    *scale = (base->STS & CORDIC_STS_SCALE_MASK) >> CORDIC_STS_SCALE_SHIFT;
    return base->OUTB;
}

/*!
 * @brief Get diff result
 *
 * This function get sqrt(x^2 - y^2) result by CORDIC.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] x The first param for diff calculation
 * @param[in] y The second param for diff calculation
 */
static inline uint32_t CORDIC_GetDiff(CORDIC_Type *base, uint32_t x, uint32_t y)
{
    base->X = x;
    base->Y = y;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    return base->OUTA;
}

/*!
 * @brief Get arctanh result
 *
 * This function get arctanh(y/x) result by CORDIC.
 * The true result need multiply to scale.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] x The divisor for arctan calculation
 * @param[in] y The dividend for arctan calculation
 * @param[out] scale The scale for result need multiply
 */
static inline uint32_t CORDIC_GetArcTanh(CORDIC_Type *base, uint32_t x, uint32_t y, uint8_t *scale)
{
    base->X = x;
    base->Y = y;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    *scale = (base->STS & CORDIC_STS_SCALE_MASK) >> CORDIC_STS_SCALE_SHIFT;
    return base->OUTB;
}

/*!
 * @brief Get exp result
 *
 * This function get e^z result by CORDIC.
 * The true result need multiply to scale.
 *
 * @param[in] base CORDIC peripheral address
 * @param[in] z power for exp calculation
 * @param[out] scale The scale for result need multiply
 */
static inline uint32_t CORDIC_GetEXP(CORDIC_Type *base, uint32_t z, uint8_t *scale)
{
    base->Z = z;
    while((base->STS & CORDIC_STS_IDLE_MASK) != CORDIC_STS_IDLE_MASK);
    *scale = (base->STS & CORDIC_STS_SCALE_MASK) >> CORDIC_STS_SCALE_SHIFT;
    return base->OUTA;
}

#if defined(__cplusplus)
}
#endif

/*! @} */

#endif /* CORDIC_DRIVER_H */
/*******************************************************************************
 * EOF
 ******************************************************************************/
