/**
  ******************************************************************************
  * @file    main.c
  * @author  MCU Application Team
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 Puya Semiconductor Co.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by Puya under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "ms32c0xx.h"

/* Private define ------------------------------------------------------------*/
#define HSI_TIMEOUT_VALUE          ((uint32_t)100)  /* 100 ms */
#define CLOCKSWITCH_TIMEOUT_VALUE  ((uint32_t)5000) /* 5 s    */

#define ERROR_HSI_TIMEOUT          0x01
#define ERROR_CLKSWITCH_TIMEOUT    0x02

#define ERROR_ERASE 0x04
#define ERROR_PROG  0x08

#define FLASH_USER_START_ADDR     0x08001000

#define FLASH_TIMMING_SEQUENCE_CONFIG()           \
                                                        do {                                            \
                                                            FLASH->TS0  = (*(uint32_t *)(0x1FFF009C))&0xFF;           \
                                                            FLASH->TS1  = ((*(uint32_t *)(0x1FFF009C))>>16)&0xFF;    \
                                                            FLASH->TS3  = ((*(uint32_t *)(0x1FFF009C))>>8)&0xFF;      \
                                                            FLASH->TS2P = (*(uint32_t *)(0x1FFF00A0))&0xFF;           \
                                                            FLASH->TPS3 = ((*(uint32_t *)(0x1FFF00A0))>>16)&0x3FF;    \
                                                            FLASH->ERTPE = (*(uint32_t *)(0x1FFF00A4))&0xFFFF;      \
                                                            FLASH->PRGTPE = (*(uint32_t *)(0x1FFF00A8))&0xFFFF;       \
                                                            FLASH->PRETPE = ((*(uint32_t *)(0x1FFF00A8))>>16)&0x3FFF; \
                                                         } while(0U)

/* Private variables ---------------------------------------------------------*/
static __IO uint32_t Tick;
volatile uint16_t error = 0; 

uint32_t DATA[64] = {0x01010101, 0x23456789, 0x3456789A, 0x456789AB, 0x56789ABC, 0x6789ABCD, 0x789ABCDE, 0x89ABCDEF,
                     0x9ABCDEF0, 0xABCDEF01, 0xBCDEF012, 0xCDEF0123, 0xDEF01234, 0xEF012345, 0xF0123456, 0x01234567,
                     0x01010101, 0x23456789, 0x3456789A, 0x456789AB, 0x56789ABC, 0x6789ABCD, 0x789ABCDE, 0x89ABCDEF,
                     0x9ABCDEF0, 0xABCDEF01, 0xBCDEF012, 0xCDEF0123, 0xDEF01234, 0xEF012345, 0xF0123456, 0x01234567,
                     0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa,
                     0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa,
                     0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa,
                     0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa, 0x55555555, 0x23456789, 0xaaaaaaaa,
                    };

/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
static __INLINE void APP_SystemClockConfig(void);
static __INLINE void APP_GPIOConfig(void);
static __INLINE void APP_FlashLock(void);
static __INLINE void APP_FlashUnlock(void);
static __INLINE void APP_FlashSectorErase(uint32_t Sector_addr);
static __INLINE uint32_t APP_FlashEraseCheck(uint32_t first_page_addr, uint32_t length);
static __INLINE void APP_FlashPageProgram(uint32_t Address, uint32_t * DataAddress);
static __INLINE uint32_t APP_FlashProgramCheck(uint32_t flash_addr, uint32_t *data, uint32_t length);

/**
  * @brief  Main program.
  * @param  None
  * @retval int
  */
int main(void)
{
  SysTick_Config(8000); /* 1ms config */
  APP_SystemClockConfig();

  RCC->APBENR1 |= (RCC_APBENR1_PWREN);  
  RCC->APBENR2 |= (RCC_APBENR2_SYSCFGEN);  

  APP_GPIOConfig();
  if (error != 0)
  {
    while(1) /* endless loop */
    {
    }
  }

  /* LED OFF */ 
  GPIOA->ODR |= (1<<1);

  /* Wait for Button pressed PA0 */ 
  while((GPIOA->IDR & 0x1) == 0)
  {
  }

  /* Flash Unlock */
  APP_FlashUnlock();

  FLASH_TIMMING_SEQUENCE_CONFIG();
  
  /* Sector Erase at FLASH_USER_START_ADDR */  
  APP_FlashSectorErase(FLASH_USER_START_ADDR);

  /* Page Erase Check */
  if(APP_FlashEraseCheck(FLASH_USER_START_ADDR,256) != 0)
  {
    while(1)
    {
    }
  }

  /* Page Program at FLASH_USER_START_ADDR */  
  APP_FlashPageProgram(FLASH_USER_START_ADDR,(uint32_t *)DATA);

  /* Page Program at FLASH_USER_START_ADDR+64 */
  APP_FlashPageProgram(FLASH_USER_START_ADDR+64,(uint32_t *)(DATA+(64/4)));

  /* Page Program at FLASH_USER_START_ADDR+128 */
  APP_FlashPageProgram(FLASH_USER_START_ADDR+128,(uint32_t *)(DATA+(128/4)));

  /* Page Program at FLASH_USER_START_ADDR+192 */
  APP_FlashPageProgram(FLASH_USER_START_ADDR+192,(uint32_t *)(DATA+(192/4)));

  /* Page Program Check */ 
  if(APP_FlashProgramCheck(FLASH_USER_START_ADDR,(uint32_t *)DATA,256) != 0)
  {
    while(1)
    {
    }
  }
 
  /* Flash Lock */
  APP_FlashLock();

  /* LED ON */ 
  GPIOA->ODR &= ~(1<<1);

  while (1) /* Infinite loop */
  {   

  }
}

/**
  * @brief  Flash Lock
  * @param  None
  * @retval None
  */
static __INLINE void APP_FlashLock()
{
  FLASH->CR |= FLASH_CR_LOCK;
}

/**
  * @brief  Flash Unlock
  * @param  None
  * @retval None
  */
static __INLINE void APP_FlashUnlock()
{
  FLASH->KEYR = FLASH_KEY1;
  FLASH->KEYR = FLASH_KEY2;
}

/**
  * @brief  FLASH Erase
  * @param  Sector_addr is the Sector Address to erase
  * @retval None
  */
static __INLINE void APP_FlashSectorErase(uint32_t Sector_addr)
{
  /* Wait Busy = 0 */
  while((FLASH->SR & FLASH_SR_BSY) != 0)
  {
  }

  /* Clear EOP */
  FLASH->SR = FLASH_SR_EOP;

  /* Enable Sector Erase */
  FLASH->CR |= FLASH_CR_SER;
  
  *(__IO uint32_t *)(Sector_addr) = 0xff;
  
  /* Wait Busy = 0 */
  while((FLASH->SR & FLASH_SR_BSY) != 0)
  {
  }
  /* Wait EOP = 1 */
  while((FLASH->SR & FLASH_SR_EOP) != 1)
  {
  }
  /* Clear EOP */
  FLASH->SR = FLASH_SR_EOP;

  /* Disable Sector Erase */
  FLASH->CR &= ~FLASH_CR_SER;
}

/**
  * @brief   This function checks that the whole page has been correctly erased.
  * @param   first_page_addr is the first address of the page to erase to check
  * @param   length is the length to check
  * @retval  None
  */
static __INLINE uint32_t APP_FlashEraseCheck(uint32_t first_page_addr, uint32_t length)
{
  uint32_t i=0;  

  for (i=0; i < length; i+=4) /* Check the erasing of the page by reading all the page value */
  {
    if (*(__IO uint32_t *)(first_page_addr + i) != (uint32_t)0xFFFFFFFF) /* compare with erased value, all bits at 1 */
    {
      error |= ERROR_ERASE; /* report the error to the main progran */
    }
  }

  return error;
}

/**
  * @brief   This function checks that the data has been correctly programmed
  * @param   flash_addr is the first address of page to be verified
  * @param   data is the data to be verified
  * @param   length is the length to check
  * @retval  None
  */
static __INLINE uint32_t APP_FlashProgramCheck(uint32_t flash_addr, uint32_t *data, uint32_t length)
{
  uint32_t i;  

  for (i = 0; i < length; i+=4, data++)
  {
    if (*(uint32_t *)(flash_addr + i) != *data)
    {
      error |= ERROR_PROG; /* report the error to the main progran */
    }
  }
  return error;
}

/**
  * @brief  FLASH Page Program
  * @param  Address page address to write
  * @param  DataAddress the data to write
  * @retval None
  */
static __INLINE void APP_FlashPageProgram(uint32_t Address, uint32_t * DataAddress)
{
  uint8_t index=0;
  uint32_t dest = Address;
  uint32_t * src = DataAddress;
  uint32_t primask_bit;

  /* Wait Busy = 0 */ 
  while((FLASH->SR & FLASH_SR_BSY) != 0)
  {
  }

  /* Clear EOP */ 
  FLASH->SR = FLASH_SR_EOP;

  /* Clear EOP */
  FLASH->CR |= FLASH_CR_PG;

  /* Enter critical section */
  primask_bit = __get_PRIMASK();
  __disable_irq();
  /* 16 words*/
  while(index<16U)
  {
    *(uint32_t *)dest = *src;
    src += 1U;
    dest += 4U;
    index++;
    if(index==15)
    {
      FLASH->CR |= FLASH_CR_PGSTRT;
    }
  }

  /* Exit critical section: restore previous priority mask */
  __set_PRIMASK(primask_bit);

  /* Wait Busy = 0 */
  while((FLASH->SR & FLASH_SR_BSY) != 0)
  {
  }
  /* Clear EOP */
  FLASH->SR = FLASH_SR_EOP;

  /* Disable Page Program */
  FLASH->CR &= ~FLASH_CR_PG;
}

/**
  * @brief  This function configures the system clock @8MHz 
  *         assuming the registers have their reset value before the call.
  *         SYSTEM CLOCK  = HSI DIV1
  *         FLASH LATENCY = 0
  * @param  None
  * @retval None
  */
static __INLINE void APP_SystemClockConfig(void)
{
  uint32_t tickstart = 0;
  /* (1) HSI DIV1 and Enable HSI */
  /* (2) Set HSI 8M and Triming Data */
  /* (3) Wait for HSI ready flag */
  /* (4) Set AHB divider:HCLK = SYSCLK */
  /* (5) Select HSISYS as system clock */
  /* (6) Wait for clock switched on HSI */
  /* (7) Set APB divider:PCLK = HCLK */

  RCC->CR = (RCC->CR &~(RCC_CR_HSIDIV)) | RCC_CR_HSION;                        /* (1) */

  RCC->ICSCR = (RCC->ICSCR & 0xFFFE0000) | (0 << RCC_ICSCR_HSI_FS_CR_Pos) | \
               ((*(uint32_t *)(0x1FFF0080)) & 0xFFFF);                         /* (2) */

  tickstart = Tick;
  while ((RCC->CR & (RCC_CR_HSIRDY)) != (RCC_CR_HSIRDY ))                      /* (3) */
  {
    if ((Tick - tickstart ) > HSI_TIMEOUT_VALUE)
    {
      error = ERROR_HSI_TIMEOUT; /* Report an error */
      return;
    }
  }

  RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_HPRE));                                  /* (4) */

  RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW)) | RCC_CFGR_SW_HSISYS;               /* (5) */
  tickstart = Tick;
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSISYS)                    /* (6) */
  {
    if ((Tick - tickstart ) > CLOCKSWITCH_TIMEOUT_VALUE)
    {
      error = ERROR_CLKSWITCH_TIMEOUT; /* Report an error */
      return;
    }
  }
 
  RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_PPRE));                                  /* (7) */
}



/**
  * @brief   This function enables the peripheral clocks on GPIO port A,
  *         configures GPIO PA1 in output mode for the Green LED pin,
  *         GPIO PA0 in input mode for the Button
  * @param   None
  * @retval  None
  */
static __INLINE void APP_GPIOConfig(void)
{  
  /* (1) Enable the peripheral clock of GPIOA */
  /* (2) Select output mode (01) on GPIOA pin 1 */
  /* (3) Select input mode (00) on GPIOA pin 0 */
  RCC->IOPENR |= RCC_IOPENR_GPIOAEN ; /* (1) */  
  GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODE1)) 
               | (GPIO_MODER_MODE1_0); /* (2) */  
  GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODE0)); /* (3) */ 
}

/******************************************************************************/
/*            Cortex-M0 Plus Processor Exceptions Handlers                    */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}


/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  Tick++;
}

/******************************************************************************/
/*                 MS32C001B Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_ms32c001bxx.s).                                             */
/******************************************************************************/

/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/


/************************ (C) COPYRIGHT Puya *****END OF FILE******************/
