/**
 *******************************************************************************
 * @file    dpm32m0xx_dsp.c.
 *
 * @brief   Source file for DSP firmware driver.
 *          This file provides firmware functions to manage the following
 *          functionalities of the Motor Turbo (DSP) peripheral :
 *           + Initialization and Configuration
 *           + Watchdog and Breakpoint management
 *           + Register operation
 *           + Interrupts, DMA and flags management
 *
 * @author  DPM
 *
 * @version V1.0.0
 *
 * @date    2023-11-01
 *
 * @verbatim
 ===============================================================================
                       ##### How to use this driver #####
 ===============================================================================
    [..]
      (#) Enable peripheral clock using the following functions
          RCC_AHBPeriphClockCmd(RCC_AHB_PERIPH_DSP, ENABLE).

      (#) Program the PC start address, PC end address (if using DMA, need
          program DMA channel, DMA trigger enable and DMA request). using the
          DSP_Init() function.

      (#) Enable the DSP using the DSP_Cmd() function.

      (#) Start run DSP calculate using DSP_SoftTrigStart(),  Monitoring
          operation status can using DSP_GetPCValue() and DSP_GetRunStatus().

      (#) When using the DMA mode.
        (+) Configure the DMA using DMA_Init() function.
        (+) Active the needed channel Request using DSP_DMADoneCmd() function.
        (+) Active the DMA trigger DSP using DSP_DMATrigCmd() function.

      (#) if you need to use DSP interrupt functions:
        (+) To activate the DSP interrupt, using DSP_IntCmd() function.
        (+) Check on DSP interrupt occur flags using DSP_GetIntFlagStatus()
            function.
        (+) Clear DSP interrupt flags using DSP_ClearIntFlag() function.

      (#) if you need to use DSP watchdog functions:
        (+) Program the watchdog time out value and system reset enable using
            DSP_WDGInit() function.
        (+) Enable the DSP watchdog function using the DSP_WDGCmd().
        (+) Enable the DSP watchdog interrupt function using the DSP_WDGIntCmd().

      (#) if you need to use DSP breakpoint functions:
        (+) Program the breakpoint value using DSP_DBGBreakpointConfig() function.
        (+) Enable the DSP breakpoint function using the DSP_DBGBreakpointCmd().
        (+) Enable the DSP breakpoint interrupt function using the
            DSP_DBGBreakpointIntCmd().

      (#) Program general purpose data register using DSP_GPRWrite() and
          DSP_GPRRead(). Program static data register DSP_SDRWrite() and
          DSP_SDRRead(). Program instruction storage register using
          DSP_INSWrite(), DSP_INSWriteBuffer(), DSP_INSRead() and
          DSP_INSReadBuffer().

 * @endverbatim
 *******************************************************************************/


#include "dpm32m0xx_dsp.h"

#if defined (DPM32M08x)

/**
 *******************************************************************************
 * @brief   Deinitializes DSP registers to default reset value.
 * @param   None.
 * @retval  None.
 ******************************************************************************/
void DSP_DeInit(void)
{
  unsigned short i = 0;

  /* Reset DSP control register. */
  DSP->CR = 0UL;
  DSP->START = 0UL;
  DSP->EWDG = 0UL;
  DSP->DEBUG = 0UL;

  for(i = 0; i < 256; i++)
  {
    if(i < 16)
    {
      DSP->GPR[i] = 0UL;
    }

    if(i < 32)
    {
      DSP->SDR[i] = 0UL;
    }

    DSP->INS[i] = 0UL;
  }
}

/**
 *******************************************************************************
 * @brief   Initializes DSP according to the specified parameters in the GPIO_InitStruct.
 * @param   [in]  DSP_InitType: Structure pointer of DSP configuration.
 * @retval  None.
 ******************************************************************************/
void DSP_Init(DSP_InitTypeStruct* DSP_InitType)
{
  uint32_t tempReg = 0;

  PARAM_ASSERT(IS_DSP_PC_START_ADDR(DSP_InitType->DSP_PcStart));
  PARAM_ASSERT(IS_DSP_PC_END_ADDR(DSP_InitType->DSP_PcEnd));
  PARAM_ASSERT(IS_DSP_DAM_CH(DSP_InitType->DSP_DMACh));
  PARAM_ASSERT(IS_FUNCTION_STATE(DSP_InitType->DSP_DMATrigEn));
  PARAM_ASSERT(IS_FUNCTION_STATE(DSP_InitType->DSP_DMADoneEn));

  tempReg = DSP->CR;

  tempReg &= ~DSP_CR_PC_START_Msk;
  tempReg |= (DSP_InitType->DSP_PcStart & 0x1FF);

  tempReg &= ~DSP_CR_PC_END_Msk;
  tempReg |= (uint32_t)((DSP_InitType->DSP_PcEnd & 0x1FF) << DSP_CR_PC_END_Pos);

  tempReg &= ~DSP_CR_DMA_TRIG_SEL_Msk;
  tempReg |= DSP_InitType->DSP_DMACh;

  if(DSP_InitType->DSP_DMATrigEn != DISABLE)
  {
    tempReg |= DSP_CR_DMA_TRIG_EN_Msk;
  }
  else
  {
    tempReg &= ~DSP_CR_DMA_TRIG_EN_Msk;
  }

  if(DSP_InitType->DSP_DMADoneEn != DISABLE)
  {
    tempReg |= DSP_CR_DONE_DE_Msk;
  }
  else
  {
    tempReg &= ~DSP_CR_DONE_DE_Msk;
  }

  DSP->CR = tempReg;
}

/**
 *******************************************************************************
 * @brief   Initialize the DSP_InitType with default parameters.
 * @param   [out]  DSP_InitType: Pointer to a DSP_InitTypeStruct structure which will be initialized.
 * @retval  None.
 ******************************************************************************/
void DSP_StructInit(DSP_InitTypeStruct* DSP_InitType)
{
  DSP_InitType->DSP_PcStart = 0x0000;
  DSP_InitType->DSP_PcEnd = 0x0000;
  DSP_InitType->DSP_DMACh = DSP_DMA_CH0;
  DSP_InitType->DSP_DMATrigEn = DISABLE;
  DSP_InitType->DSP_DMADoneEn = DISABLE;
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP function.
 * @param   NewState: This parameter can be: ENABLE or DISABLE
 * @retval  None.
 ******************************************************************************/
void DSP_Cmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DSP functions. */
    DSP->CR |= DSP_CR_EN_Msk;
  }
  else
  {
    /* Disable DSP functions. */
    DSP->CR &= ~DSP_CR_EN_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP interrupt.
 * @param   IntType: @ref DSP_IntTypeEnum.
 *            @arg  DSP_INT_TYPE_DIV_ZERO: Div zero interrupt.
 *            @arg  DSP_INT_TYPE_UDR: Repeated trigger interrupt.
 *            @arg  DSP_INT_TYPE_INS_ERR: Instruct error interrupt.
 *            @arg  DSP_INT_TYPE_DONE: Run completed interrupt.
 * @param   NewState: This parameter can be: ENABLE or DISABLE.
 * @retval  None.
 ******************************************************************************/
void DSP_IntCmd(DSP_IntTypeEnum DSP_IntType, FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DSP functions. */
    DSP->CR |= DSP_IntType;
  }
  else
  {
    /* Disable DSP functions. */
    DSP->CR &= ~DSP_IntType;
  }
}

/**
 *******************************************************************************
 * @brief   Get specifies DSP interrupt enable status.
 * @param   IntType: @ref DSP_IntTypeEnum.
 *            @arg  DSP_INT_TYPE_DIV_ZERO: Div zero interrupt.
 *            @arg  DSP_INT_TYPE_UDR: Repeated trigger interrupt.
 *            @arg  DSP_INT_TYPE_INS_ERR: Instruct error interrupt.
 *            @arg  DSP_INT_TYPE_DONE: Run completed interrupt.
 * @retval  FunctionalState: The DSP interrupt state(ENABLE or DISABLE).
 ******************************************************************************/
FunctionalState DSP_GetIntCmdStatus(DSP_IntTypeEnum DSP_IntType)
{
  /* Return the status of the interrupt enable bit. */
  return (FunctionalState)((DSP->CR & (DSP_IntType))
                           ? ENABLE : DISABLE);
}

/**
 *******************************************************************************
 * @brief   Enable or disable DMA channel transmission completion triggers
 *          DSP to start running.
 * @param   NewState: This parameter can be: ENABLE or DISABLE
 * @retval  None.
 ******************************************************************************/
void DSP_DMATrigCmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    DSP->CR |= DSP_CR_DMA_TRIG_EN_Msk;
  }
  else
  {
    DSP->CR &= ~DSP_CR_DMA_TRIG_EN_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP run completion triggers DMA transmission.
 * @param   NewState: This parameter can be: ENABLE or DISABLE
 * @retval  None.
 ******************************************************************************/
void DSP_DMADoneCmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    DSP->CR |= DSP_CR_DONE_DE_Msk;
  }
  else
  {
    DSP->CR &= ~DSP_CR_DONE_DE_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Software triggers DSP run.
 * @param   None.
 * @retval  None.
 ******************************************************************************/
void DSP_SoftTrigStart(void)
{
  DSP->START = DSP_START_START_Msk;
}

/**
 *******************************************************************************
 * @brief   Get current running PC value of the DSP.
 * @param   None.
 * @retval  Current running PC value,This retval between 0x0000 and 0x1FF.
 ******************************************************************************/
uint16_t DSP_GetPCValue(void)
{
  return ((DSP->SR & DSP_SR_PC_Msk) >> DSP_SR_PC_Pos);
}

/**
 *******************************************************************************
 * @brief   Get current DSP status.
 * @param   None.
 * @retval  Current DSP status: @ref DSP_RunStatusEnum
 *            @arg  DSP_STATUS_RUNNING
 *            @arg  DSP_STATUS_IDLE.
 ******************************************************************************/
DSP_RunStatusEnum DSP_GetRunStatus(void)
{
  if(RESET != (DSP->SR & DSP_SR_BUSY_Msk))
  {
    /* DSP is running. */
    return DSP_STATUS_RUNNING;
  }
  else
  {
    /* DSP is idle. */
    return DSP_STATUS_IDLE;
  }
}

/**
 *******************************************************************************
 * @brief   Get DSP interrupt flag.
 * @param   DSP_IntFlag: @ref DSP_IntFlagEnum
 *            @arg  DSP_INT_FLAG_DIV_ZERO  DSP div zero interrupt flag.
 *            @arg  DSP_INT_FLAG_DONE      DSP run completed interrupt flag.
 *            @arg  DSP_INT_FLAG_WDG       DSP watchdog overflow interrupt flag.
 *            @arg  DSP_INT_FLAG_BKPT      DSP break point interrupt flag.
 *            @arg  DSP_INT_FLAG_UDR       DSP repeated trigger flag.
 *            @arg  DSP_INT_FLAG_INS_ERR   DSP instruct error interrupt flag.
 * @retval  FlagState: Current DSP interrutp status.  @ref FlagState
 *            @arg  RESET  Interrupt flag not set
 *            @arg  SET    Interrupt flag is set.
 ******************************************************************************/
FlagState DSP_GetIntFlagStatus(DSP_IntFlagEnum DSP_IntFlag)
{
  FlagState state = RESET;

  if(RESET != (DSP->SR & ((uint32_t)DSP_IntFlag)))
  {
    /* Interrupt has occurred. */
    state = SET;
  }
  else
  {
    /* Interrupt has not occurred. */
    state = RESET;
  }

  /* Return the status of the interrupt flag. */
  return state;
}

/**
 *******************************************************************************
 * @brief   Clear DSP interrupt flag.
 * @param   DSP_IntFlag: @reg DSP_IntFlagEnum
 *            @arg  DSP_INT_FLAG_DIV_ZERO  DSP div zero interrupt flag.
 *            @arg  DSP_INT_FLAG_DONE      DSP run completed interrupt flag.
 *            @arg  DSP_INT_FLAG_WDG       DSP watchdog overflow interrupt flag.
 *            @arg  DSP_INT_FLAG_BKPT      DSP break point interrupt flag.
 *            @arg  DSP_INT_FLAG_UDR       DSP repeated trigger flag.
 *            @arg  DSP_INT_FLAG_INS_ERR   DSP instruct error interrupt flag.
 * @retval  None.
 ******************************************************************************/
void DSP_ClearIntFlag(DSP_IntFlagEnum DSP_IntFlag)
{
  /* Clear interrupt flags.*/
  DSP->SR = ((uint32_t)DSP_IntFlag);
}

/**
 *******************************************************************************
 * @brief   Initializes DSP watchdog.
 * @param   DSP_WDGInitType: @ref DSP_WDGInitTypeStruct
 *            @arg  DSP_WDGTimOutVal  Watchdog maximum count value.
 *                  This parameter can be a number between 0x0000 and 0x3FF.
 *            @arg  DSP_WDGSystemRstEn @ref FunctionalState.
 *                  @arg  DISABLE  Disable wdtchdog count overflow request system reset.
 *                  @arg  ENABLE   Enable watchdog count overflow request system reset.
 * @retval  None.
 ******************************************************************************/
void DSP_WDGInit(DSP_WDGInitTypeStruct* DSP_WDGInitType)
{
  uint32_t tempReg = 0U;

  tempReg = DSP->EWDG;

  tempReg &= ~DSP_EWDG_TIM_OUT_VAL_Msk;

  tempReg |= DSP_WDGInitType->DSP_WDGTimOutVal;

  if(DISABLE != DSP_WDGInitType->DSP_WDGSystemRstEn)
  {
    /* Enable watchdog counter overflow request system reset. */
    tempReg |= DSP_EWDG_SYS_RST_EN_Msk;
  }
  else
  {
    /* Disable watchdog counter overflow request system reset. */
    tempReg &= ~DSP_EWDG_SYS_RST_EN_Msk;
  }

  /* Store the new value. */
  DSP->EWDG = tempReg;
}

/**
 *******************************************************************************
 * @brief   Initialize the DSP_WDGInitType with default parameters.
 * @param   [out]  DSP_WDGInitType: Pointer to a DSP_WDGInitTypeStruct structure
 *                 which will be initialized.
 * @retval  None.
 ******************************************************************************/
void DSP_WDGStructInit(DSP_WDGInitTypeStruct* DSP_WDGInitType)
{
  DSP_WDGInitType->DSP_WDGTimOutVal = 0x0000;
  DSP_WDGInitType->DSP_WDGSystemRstEn = DISABLE;
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP watchdog.
 * @param   NewState: @ref FunctionalState
 * @retval  None.
 ******************************************************************************/
void DSP_WDGCmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DSP EWDG functions. */
    DSP->EWDG |= DSP_EWDG_EWDG_EN_Msk;
  }
  else
  {
    /* Disable DSP EWDG functions. */
    DSP->EWDG &= ~DSP_EWDG_EWDG_EN_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP watchdog interrupt.
 * @param   NewState: @ref FunctionalState
 * @retval  None.
 ******************************************************************************/
void DSP_WDGIntCmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DSP EWDG IE. */
    DSP->EWDG |= DSP_EWDG_EWDG_IE_Msk;
  }
  else
  {
    /* Disable DSP EWDG IE. */
    DSP->EWDG &= ~DSP_EWDG_EWDG_IE_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Config DSP debug break point value.
 * @param   DSP_DBGBkpValue: Stop run when PC equal to break point.
 *          This parameter can be a number between 0x0000 and 0x1FF
 * @retval  None.
 ******************************************************************************/
void DSP_DBGBreakpointConfig(uint16_t DSP_DBGBkpValue)
{
  uint32_t tempReg = 0U;

  tempReg = DSP->DEBUG;

  tempReg &= (~DSP_DEBUG_BREAK_POINT_Msk);

  tempReg |= (((uint32_t)DSP_DBGBkpValue) & DSP_DEBUG_BREAK_POINT_Msk);

  DSP->DEBUG = tempReg;
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP debug break point.
 * @param   NewState: @ref FunctionalState
 * @retval  None.
 ******************************************************************************/
void DSP_DBGBreakpointCmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DSP DBG function. */
    DSP->DEBUG |= DSP_DEBUG_BREAK_POINT_EN_Msk;
  }
  else
  {
    /* Disable DSP DBG function. */
    DSP->DEBUG &= ~DSP_DEBUG_BREAK_POINT_EN_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Enable or disable DSP debug break point interrupt.
 * @param   NewState: @ref FunctionalState
 * @retval  None.
 ******************************************************************************/
void DSP_DBGBreakpointIntCmd(FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable DSP DBG function. */
    DSP->DEBUG |= DSP_DEBUG_BREAK_POINT_IE_Msk;
  }
  else
  {
    /* Disable DSP DBG function. */
    DSP->DEBUG &= ~DSP_DEBUG_BREAK_POINT_IE_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Write DSP GPRx value.
 * @param   GPRx: @ref DSP_GPRxEnum.
 * @param   GPR_Data: Write value.
 * @retval  None.
 ******************************************************************************/
void DSP_GPRWrite(DSP_GPRxEnum GPRx, int16_t GPR_Data)
{
  if(GPRx > DSP_GPR15)
  {
    return;
  }

  DSP->GPR[GPRx] = (uint32_t)GPR_Data;
}

/**
 *******************************************************************************
 * @brief   Read DSP GPRx value.
 * @param   GPRx: @ref DSP_GPRxEnum.
 * @retval  Current GPRx value.
 ******************************************************************************/
int16_t DSP_GPRRead(DSP_GPRxEnum GPRx)
{
  if(GPRx > DSP_GPR15)
  {
    return 0;
  }

  return (int16_t)(DSP->GPR[GPRx]);
}

/**
 *******************************************************************************
 * @brief   Write DSP SDRx value.
 * @param   SDRx: @ref DSP_SDRxEnum.
 * @param   SDR_Data: Write value.
 * @retval  None.
 ******************************************************************************/
void DSP_SDRWrite(DSP_SDRxEnum SDRx, int16_t SDR_Data)
{
  if(SDRx > DSP_SDR31)
  {
    return;
  }

  DSP->SDR[SDRx] = (uint32_t)SDR_Data;
}

/**
 *******************************************************************************
 * @brief   Read DSP SDRx value.
 * @param   SDRx: @ref DSP_SDRxEnum.
 * @retval  Current SDRx value.
 ******************************************************************************/
int16_t DSP_SDRRead(DSP_SDRxEnum SDRx)
{
  if(SDRx > DSP_SDR31)
  {
    return 0;
  }

  return (int16_t)(DSP->SDR[SDRx]);
}

/**
 *******************************************************************************
 * @brief   Write DSP single INS value.
 * @param   INSx: Each INSx contains two instructions
 *          This parameter can be a number between 0x00 and 0xFF.
 * @param   INS_Data: Write instruct value.
 * @retval  None.
 ******************************************************************************/
void DSP_INSWrite(uint8_t INSx, uint32_t INS_Data)
{
  DSP->INS[INSx] = INS_Data;
}

/**
 *******************************************************************************
 * @brief   Write DSP INS buffer.
 * @param   startINSx: Start instruct address.
 *          This parameter can be a number between 0 and 255.
 * @param   INS_Data: Write instructs buffer address.
 * @param   InsLen: Write instruct number.
 *          This parameter can be a number between 0 and 256.
 * @retval  None.
 ******************************************************************************/
void DSP_INSWriteBuffer(uint8_t startINSx, uint32_t *INS_Data, uint16_t InsLen)
{
  unsigned short i = 0;

  if(startINSx + InsLen > 256)
  {
    return;
  }

  for(i = 0; i < InsLen; i++)
  {
    DSP->INS[startINSx + i] = INS_Data[i];
  }
}

/**
 *******************************************************************************
 * @brief   Read DSP INSx value.
 * @param   INSx: Each INSx contains two instructions
 *          This parameter can be a number between 0x00 and 0xFF.
 * @retval  Current INSx value.
 ******************************************************************************/
uint32_t DSP_INSRead(uint8_t INSx)
{
  return DSP->INS[INSx];
}

/**
 *******************************************************************************
 * @brief   Read DSP INS buffer.
 * @param   startINSx: Start instruct address.
 *          This parameter can be a number between 0 and 255.
 * @param   INS_Data: Read instructs buffer address.
 * @param   InsLen: Read instruct number.
 *          This parameter can be a number between 0 and 256.
 * @retval  None.
 ******************************************************************************/
void DSP_INSReadBuffer(uint8_t startINSx, uint32_t *INS_Data, uint16_t InsLen)
{
  unsigned short i = 0;

  if(startINSx + InsLen > 256)
  {
    return;
  }

  for(i = 0; i < InsLen; i++)
  {
    INS_Data[i] = DSP->INS[startINSx + i];
  }
}

#endif /* DPM32M08X*/
