text: /*!
   \file    system_gd32vf103.h
  \brief   RISC-V Device Peripheral Access Layer Source File for
            GD32VF103 Device Series
  
  */
  
  /*
      Copyright (c) 2020, GigaDevice Semiconductor Inc.
  
      Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:
  
      1. Redistributions of source code must retain the above copyright notice, this
         list of conditions and the following disclaimer.
      2. Redistributions in binary form must reproduce the above copyright notice,
         this list of conditions and the following disclaimer in the documentation
         and/or other materials provided with the distribution.
      3. Neither the name of the copyright holder nor the names of its contributors
         may be used to endorse or promote products derived from this software without
         specific prior written permission.
  
      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  OF SUCH DAMAGE.
  */
  
  /* This file refers the RISC-V standard, some adjustments are made according to GigaDevice chips */
  #include "board.h"
  
  /* system frequency define */
  #define __IRC8M           (IRC8M_VALUE)            /* internal 8 MHz RC oscillator frequency */
  #define __HXTAL           (HXTAL_VALUE)            /* high speed crystal oscillator frequency */
  #define __SYS_OSC_CLK     (__IRC8M)                /* main oscillator frequency */
  #define __SYSTEM_CLOCK_HXTAL (HXTAL_VALUE)
  
  #if !defined(__SYSTEM_CLOCK)
  #define __SYSTEM_CLOCK 72000000
  #endif
  
  #if __SYSTEM_CLOCK == 48000000
  #define __SYSTEM_CLOCK_48M_PLL_HXTAL (uint32_t)(48000000)
  uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_HXTAL;
  static void system_clock_48m_hxtal(void);
  
  #elif __SYSTEM_CLOCK == 72000000
  #define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000)
  uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL;
  static void system_clock_72m_hxtal(void);
  
  #elif __SYSTEM_CLOCK == 96000000
  #define __SYSTEM_CLOCK_96M_PLL_HXTAL (uint32_t)(96000000)
  uint32_t SystemCoreClock = __SYSTEM_CLOCK_96M_PLL_HXTAL;
  static void system_clock_96m_hxtal(void);
  
  #else
  #error No valid system clock configuration set!
  #endif
  
    /* configure the system clock */
    static void system_clock_config(void);
  
    /*!
        \brief      configure the system clock
        \param[in]  none
        \param[out] none
        \retval     none
    */
    static void system_clock_config(void) {
  #if defined(__SYSTEM_CLOCK_48M_PLL_HXTAL)
    system_clock_48m_hxtal();
  #elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
      system_clock_72m_hxtal();
  #elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
    system_clock_96m_hxtal();
  #endif /* __SYSTEM_CLOCK_HXTAL */
  }
  
  /*!
      \brief      setup the microcontroller system, initialize the system
      \param[in]  none
      \param[out] none
      \retval     none
  */
  void SystemInit(void)
  {
      /* reset the RCC clock configuration to the default reset state */
      /* enable IRC8M */
      RCU_CTL |= RCU_CTL_IRC8MEN;
      
      /* reset SCS, AHBPSC, APB1PSC, APB2PSC, ADCPSC, CKOUT0SEL bits */
      RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC |
                    RCU_CFG0_ADCPSC | RCU_CFG0_ADCPSC_2 | RCU_CFG0_CKOUT0SEL);
  
      /* reset HXTALEN, CKMEN, PLLEN bits */
      RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN);
  
      /* Reset HXTALBPS bit */
      RCU_CTL &= ~(RCU_CTL_HXTALBPS);
  
      /* reset PLLSEL, PREDV0_LSB, PLLMF, USBFSPSC bits */
      
      RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF |
                    RCU_CFG0_USBFSPSC | RCU_CFG0_PLLMF_4);
      RCU_CFG1 = 0x00000000U;
  
      /* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */
      RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);
      /* disable all interrupts */
      RCU_INT = 0x00FF0000U;
  
      /* Configure the System clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */
      system_clock_config();
  }
  
  /*!
      \brief      update the SystemCoreClock with current core clock retrieved from cpu registers
      \param[in]  none
      \param[out] none
      \retval     none
  */
  void SystemCoreClockUpdate(void)
  {
      uint32_t scss;
      uint32_t pllsel, predv0sel, pllmf, ck_src;
      uint32_t predv0, predv1, pll1mf;
  
      scss = GET_BITS(RCU_CFG0, 2, 3);
  
      switch (scss)
      {
          /* IRC8M is selected as CK_SYS */
          case SEL_IRC8M:
              SystemCoreClock = IRC8M_VALUE;
              break;
              
          /* HXTAL is selected as CK_SYS */
          case SEL_HXTAL:
              SystemCoreClock = HXTAL_VALUE;
              break;
              
          /* PLL is selected as CK_SYS */
          case SEL_PLL:
              /* PLL clock source selection, HXTAL or IRC8M/2 */
              pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL);
  
  
              if(RCU_PLLSRC_IRC8M_DIV2 == pllsel){
                  /* PLL clock source is IRC8M/2 */
                  ck_src = IRC8M_VALUE / 2U;
              }else{
                  /* PLL clock source is HXTAL */
                  ck_src = HXTAL_VALUE;
  
                  predv0sel = (RCU_CFG1 & RCU_CFG1_PREDV0SEL);
  
                  /* source clock use PLL1 */
                  if(RCU_PREDV0SRC_CKPLL1 == predv0sel){
                      predv1 = ((RCU_CFG1 & RCU_CFG1_PREDV1) >> 4) + 1U;
                      pll1mf = ((RCU_CFG1 & RCU_CFG1_PLL1MF) >> 8) + 2U;
                      if(17U == pll1mf){
                          pll1mf = 20U;
                      }
                      ck_src = (ck_src / predv1) * pll1mf;
                  }
                  predv0 = (RCU_CFG1 & RCU_CFG1_PREDV0) + 1U;
                  ck_src /= predv0;
              }
  
              /* PLL multiplication factor */
              pllmf = GET_BITS(RCU_CFG0, 18, 21);
  
              if((RCU_CFG0 & RCU_CFG0_PLLMF_4)){
                  pllmf |= 0x10U;
              }
  
              if(pllmf >= 15U){
                  pllmf += 1U;
              }else{
                  pllmf += 2U;
              }
  
              SystemCoreClock = ck_src * pllmf;
  
              if(15U == pllmf){
                  /* PLL source clock multiply by 6.5 */
                  SystemCoreClock = ck_src * 6U + ck_src / 2U;
              }
  
              break;
  
          /* IRC8M is selected as CK_SYS */
          default:
              SystemCoreClock = IRC8M_VALUE;
              break;
      }
  }
  
  #if defined(__SYSTEM_CLOCK_48M_PLL_HXTAL)
  /*!
      \brief      configure the system clock to 48M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
      \param[in]  none
      \param[out] none
      \retval     none
  */
  static void system_clock_48m_hxtal(void)
  {
      uint32_t timeout = 0U;
      uint32_t stab_flag = 0U;
  
      /* enable HXTAL */
      RCU_CTL |= RCU_CTL_HXTALEN;
  
      /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
      do{
          timeout++;
          stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
      }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
  
      /* if fail */
      if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
          while(1){
          }
      }
  
      /* HXTAL is stable */
      /* AHB = SYSCLK */
      RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
      /* APB2 = AHB/1 */
      RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
      /* APB1 = AHB/2 */
      RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
  
      /* CK_PLL = (CK_PREDIV0) * 12 = 48 MHz */
      RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
      RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL12);
  
      if(HXTAL_VALUE==25000000){
  
          /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
          RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
          RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
  
          /* enable PLL1 */
          RCU_CTL |= RCU_CTL_PLL1EN;
          /* wait till PLL1 is ready */
          while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
          }
  
      }else if(HXTAL_VALUE==8000000){
          RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
          RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
      }
  
  
  
      /* enable PLL */
      RCU_CTL |= RCU_CTL_PLLEN;
  
      /* wait until PLL is stable */
      while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
      }
  
      /* select PLL as system clock */
      RCU_CFG0 &= ~RCU_CFG0_SCS;
      RCU_CFG0 |= RCU_CKSYSSRC_PLL;
  
      /* wait until PLL is selected as system clock */
      while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
      }
  }
  
  #elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
  /*!
      \brief      configure the system clock to 72M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
      \param[in]  none
      \param[out] none
      \retval     none
  */
  static void system_clock_72m_hxtal(void)
  {
      uint32_t timeout = 0U;
      uint32_t stab_flag = 0U;
  
      /* enable HXTAL */
      RCU_CTL |= RCU_CTL_HXTALEN;
  
      /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
      do{
          timeout++;
          stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
      }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
  
      /* if fail */
      if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
          while(1){
          }
      }
  
      /* HXTAL is stable */
      /* AHB = SYSCLK */
      RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
      /* APB2 = AHB/1 */
      RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
      /* APB1 = AHB/2 */
      RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
  
      /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */ 
      RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
      RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL18);
  
  
      if(HXTAL_VALUE==25000000){
  
          /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
          RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
          RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
  
          /* enable PLL1 */
          RCU_CTL |= RCU_CTL_PLL1EN;
          /* wait till PLL1 is ready */
          while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
          }
  
      }else if(HXTAL_VALUE==8000000){
          RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
          RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
      }
  
      /* enable PLL */
      RCU_CTL |= RCU_CTL_PLLEN;
  
      /* wait until PLL is stable */
      while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
      }
  
      /* select PLL as system clock */
      RCU_CFG0 &= ~RCU_CFG0_SCS;
      RCU_CFG0 |= RCU_CKSYSSRC_PLL;
  
      /* wait until PLL is selected as system clock */
      while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
      }
  }
  
  #elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
  /*!
      \brief      configure the system clock to 96M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
      \param[in]  none
      \param[out] none
      \retval     none
  */
  static void system_clock_96m_hxtal(void)
  {
      uint32_t timeout = 0U;
      uint32_t stab_flag = 0U;
  
      /* enable HXTAL */
      RCU_CTL |= RCU_CTL_HXTALEN;
  
      /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
      do{
          timeout++;
          stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
      }while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
  
      /* if fail */
      if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
          while(1){
          }
      }
  
      /* HXTAL is stable */
      /* AHB = SYSCLK */
      RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
      /* APB2 = AHB/1 */
      RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
      /* APB1 = AHB/2 */
      RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
  
      if(HXTAL_VALUE==25000000){
  
          /* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */
          RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
          RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24);
  
          /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
          RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
          RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
          /* enable PLL1 */
          RCU_CTL |= RCU_CTL_PLL1EN;
          /* wait till PLL1 is ready */
          while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
          }
  
      }else if(HXTAL_VALUE==8000000){
          /* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */
          RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
          RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24);
  
          RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PREDV1 | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV0);
          RCU_CFG1 |= (RCU_PREDV0SRC_HXTAL | RCU_PREDV0_DIV2 );
      }
  
      /* enable PLL */
      RCU_CTL |= RCU_CTL_PLLEN;
  
      /* wait until PLL is stable */
      while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
      }
  
      /* select PLL as system clock */
      RCU_CFG0 &= ~RCU_CFG0_SCS;
      RCU_CFG0 |= RCU_CKSYSSRC_PLL;
  
      /* wait until PLL is selected as system clock */
      while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
      }
  }
  
  #endif
  
  /**
   * \defgroup  NMSIS_Core_IntExcNMI_Handling   Interrupt and Exception and NMI Handling
   * \brief Functions for interrupt, exception and nmi handle available in system_<device>.c.
   * \details
   * Nuclei provide a template for interrupt, exception and NMI handling. Silicon Vendor could adapat according
   * to their requirement. Silicon vendor could implement interface for different exception code and
   * replace current implementation.
   *
   * @{
   */
  /** \brief Max exception handler number, don't include the NMI(0xFFF) one */
  #define MAX_SYSTEM_EXCEPTION_NUM        12
  /**
   * \brief      Store the exception handlers for each exception ID
   * \note
   * - This SystemExceptionHandlers are used to store all the handlers for all
   * the exception codes Nuclei N/NX core provided.
   * - Exception code 0 - 11, totally 12 exceptions are mapped to SystemExceptionHandlers[0:11]
   * - Exception for NMI is also re-routed to exception handling(exception code 0xFFF) in startup code configuration, the handler itself is mapped to SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM]
   */
  static unsigned long SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM + 1];
  
  /**
   * \brief      Exception Handler Function Typedef
   * \note
   * This typedef is only used internal in this system_gd32vf103.c file.
   * It is used to do type conversion for registered exception handler before calling it.
   */
  typedef void (*EXC_HANDLER)(unsigned long mcause, unsigned long sp);
  
  /**
   * \brief      System Default Exception Handler
   * \details
   * This function provided a default exception and NMI handling code for all exception ids.
   * By default, It will just print some information for debug, Vendor can customize it according to its requirements.
   */
  static void system_default_exception_handler(unsigned long mcause, unsigned long sp)
  {
      /* TODO: Uncomment this if you have implement printf function */
      /*printf("MCAUSE: 0x%lx\r\n", mcause);
      printf("MEPC  : 0x%lx\r\n", __RV_CSR_READ(CSR_MEPC));
      printf("MTVAL : 0x%lx\r\n", __RV_CSR_READ(CSR_MBADADDR));*/
      while (1);
  }
  
  /**
   * \brief      Initialize all the default core exception handlers
   * \details
   * The core exception handler for each exception id will be initialized to \ref system_default_exception_handler.
   * \note
   * Called in \ref _init function, used to initialize default exception handlers for all exception IDs
   */
  static void Exception_Init(void)
  {
      for (int i = 0; i < MAX_SYSTEM_EXCEPTION_NUM + 1; i++) {
          SystemExceptionHandlers[i] = (unsigned long)system_default_exception_handler;
      }
  }
  
  /**
   * \brief       Register an exception handler for exception code EXCn
   * \details
   * * For EXCn < \ref MAX_SYSTEM_EXCEPTION_NUM, it will be registered into SystemExceptionHandlers[EXCn-1].
   * * For EXCn == NMI_EXCn, it will be registered into SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM].
   * \param   EXCn    See \ref EXCn_Type
   * \param   exc_handler     The exception handler for this exception code EXCn
   */
  void Exception_Register_EXC(uint32_t EXCn, unsigned long exc_handler)
  {
      if ((EXCn < MAX_SYSTEM_EXCEPTION_NUM) && (EXCn != 0)) {
          SystemExceptionHandlers[EXCn] = exc_handler;
      } else if (EXCn == NMI_EXCn) {
          SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM] = exc_handler;
      }
  }
  
  /**
   * \brief       Get current exception handler for exception code EXCn
   * \details
   * * For EXCn < \ref MAX_SYSTEM_EXCEPTION_NUM, it will return SystemExceptionHandlers[EXCn-1].
   * * For EXCn == NMI_EXCn, it will return SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM].
   * \param   EXCn    See \ref EXCn_Type
   * \return  Current exception handler for exception code EXCn, if not found, return 0.
   */
  unsigned long Exception_Get_EXC(uint32_t EXCn)
  {
      if ((EXCn < MAX_SYSTEM_EXCEPTION_NUM) && (EXCn != 0)) {
          return SystemExceptionHandlers[EXCn];
      } else if (EXCn == NMI_EXCn) {
          return SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM];
      } else {
          return 0;
      }
  }
  
  /**
   * \brief      Common NMI and Exception handler entry
   * \details
   * This function provided a command entry for NMI and exception. Silicon Vendor could modify
   * this template implementation according to requirement.
   * \remarks
   * - RISCV provided common entry for all types of exception. This is proposed code template
   *   for exception entry function, Silicon Vendor could modify the implementation.
   * - For the core_exception_handler template, we provided exception register function \ref Exception_Register_EXC
   *   which can help developer to register your exception handler for specific exception number.
   */
  uint32_t core_exception_handler(unsigned long mcause, unsigned long sp)
  {
      uint32_t EXCn = (uint32_t)(mcause & 0X00000fff);
      EXC_HANDLER exc_handler;
  
      if ((EXCn < MAX_SYSTEM_EXCEPTION_NUM) && (EXCn > 0)) {
          exc_handler = (EXC_HANDLER)SystemExceptionHandlers[EXCn];
      } else if (EXCn == NMI_EXCn) {
          exc_handler = (EXC_HANDLER)SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM];
      } else {
          exc_handler = (EXC_HANDLER)system_default_exception_handler;
      }
      if (exc_handler != NULL) {
          exc_handler(mcause, sp);
      }
      return 0;
  }
  /** @} */ /* End of Doxygen Group NMSIS_Core_ExceptionAndNMI */
  
  /**
   * \brief initialize eclic config
   * \details
   * Eclic need initialize after boot up, Vendor could also change the initialization
   * configuration.
   */
  void ECLIC_Init(void)
  {
      /* TODO: Add your own initialization code here. This function will be called by main */
      ECLIC_SetMth(0);
      ECLIC_SetCfgNlbits(__ECLIC_INTCTLBITS);
  }
  
  /**
   * \brief  Initialize a specific IRQ and register the handler
   * \details
   * This function set vector mode, trigger mode and polarity, interrupt level and priority,
   * assign handler for specific IRQn.
   * \param [in]  IRQn        NMI interrupt handler address
   * \param [in]  shv         \ref ECLIC_NON_VECTOR_INTERRUPT means non-vector mode, and \ref ECLIC_VECTOR_INTERRUPT is vector mode
   * \param [in]  trig_mode   see \ref ECLIC_TRIGGER_Type
   * \param [in]  lvl         interupt level
   * \param [in]  priority    interrupt priority
   * \param [in]  handler     interrupt handler, if NULL, handler will not be installed
   * \return       -1 means invalid input parameter. 0 means successful.
   * \remarks
   * - This function use to configure specific eclic interrupt and register its interrupt handler and enable its interrupt.
   * - If the vector table is placed in read-only section(FLASHXIP mode), handler could not be installed
   */
  int32_t ECLIC_Register_IRQ(IRQn_Type IRQn, uint8_t shv, ECLIC_TRIGGER_Type trig_mode, uint8_t lvl, uint8_t priority, void* handler)
  {
      if ((IRQn > SOC_INT_MAX) || (shv > ECLIC_VECTOR_INTERRUPT) \
          || (trig_mode > ECLIC_NEGTIVE_EDGE_TRIGGER)) {
          return -1;
      }
  
      /* set interrupt vector mode */
      ECLIC_SetShvIRQ(IRQn, shv);
      /* set interrupt trigger mode and polarity */
      ECLIC_SetTrigIRQ(IRQn, trig_mode);
      /* set interrupt level */
      ECLIC_SetLevelIRQ(IRQn, lvl);
      /* set interrupt priority */
      ECLIC_SetPriorityIRQ(IRQn, priority);
      if (handler != NULL) {
          /* set interrupt handler entry to vector table */
          ECLIC_SetVector(IRQn, (rv_csr_t)handler);
      }
      /* enable interrupt */
      ECLIC_EnableIRQ(IRQn);
      return 0;
  }
  /** @} */ /* End of Doxygen Group NMSIS_Core_ExceptionAndNMI */
  
  /**
   * \brief early init function before main
   * \details
   * This function is executed right before main function.
   * For RISC-V gnu toolchain, _init function might not be called
   * by __libc_init_array function, so we defined a new function
   * to do initialization
   */
  void _premain_init(void) {
    /* Initialize exception default handlers */
    Exception_Init();
    /* ECLIC initialization, mainly MTH and NLBIT */
    ECLIC_Init();
  }
  
  /**
   * \brief finish function after main
   * \param [in]  status     status code return from main
   * \details
   * This function is executed right after main function.
   * For RISC-V gnu toolchain, _fini function might not be called
   * by __libc_fini_array function, so we defined a new function
   * to do initialization
   */
  void _postmain_fini(int status)
  {
      /* TODO: Add your own finishing code here, called after main */
  }
  
  /**
   * \brief _init function called in __libc_init_array()
   * \details
   * This `__libc_init_array()` function is called during startup code,
   * user need to implement this function, otherwise when link it will
   * error init.c:(.text.__libc_init_array+0x26): undefined reference to `_init'
   * \note
   * Please use \ref _premain_init function now
   */
  void _init(void)
  {
      /* Don't put any code here, please use _premain_init now */
  }
  
  /**
   * \brief _fini function called in __libc_fini_array()
   * \details
   * This `__libc_fini_array()` function is called when exit main.
   * user need to implement this function, otherwise when link it will
   * error fini.c:(.text.__libc_fini_array+0x28): undefined reference to `_fini'
   * \note
   * Please use \ref _postmain_fini function now
   */
  void _fini(void)
  {
      /* Don't put any code here, please use _postmain_fini now */
  }
  
  /** @} */ /* End of Doxygen Group NMSIS_Core_SystemAndClock */
  