/**
  ******************************************************************************
  * @file    flash.c
  * @author  Puya Application Team
  * @brief   Contains FLASH HW configuration
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 Puya Semiconductor.
  * All rights reserved.</center></h2>
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "flash.h"
#include "usart.h"
#include "bootloader.h"
#include "wdg.h"

/**
  * @brief  This function is used to write data in FLASH memory.
  * @param  dwAddr The address where that data will be written.
  * @param  pucDataBuf The data to be written.
  * @param  ucDataLength The length of the data to be written.
  * @retval An ErrorStatus enumeration value:
  *          - RET_SUCCESS_ACK: Write FLASH operation done
  *          - RET_ERROR_NACK:   Write FLASH operation failed or the value of one parameter is not ok
  */
ErrorStatus WriteFlash(uint32_t dwAddr, uint8_t* pucDataBuf, uint8_t ucDataLength)
{
#ifdef CHECK_WRP
  uint8_t ucSector;
#endif
  uint16_t i, j;
  uint16_t wDataLength;
  uint32_t dwOffsetAddr;
  ErrorStatus eResultFlag;

#ifdef ADJUST_ADDR_AND_SIZE
  uint32_t dwOrgAddr;//备份调整前的地址
  uint32_t dwEndAddr;//调整后的结束地址

  wDataLength = ucDataLength + 1;
  /*
    参考uISP工具:
    当地为0x08000000，大小为0xC8时，调整后的起始地址为0x08000000，结束地址为0x080000FF，所以大小为0x100
    当地为0x080000C8，大小为0xC8时，调整后的起始地址为0x08000080，结束地址为0x080001FF，所以大小为0x180
    当地为0x08000190，大小为0xC8时，调整后的起始地址为0x08000180，结束地址为0x0800027F，所以大小为0x100
  */
  dwOrgAddr = dwAddr;//备份调整前的地址

  if (dwAddr&0x7F)//起始地址不是按照128字节对齐，需要调整
  {
    CLEAR_BIT(dwAddr, 0x7F);//按128字节对方方式调整得到起始地址

    //数据向后偏移(dwOrgAddr-dwAddr)字节, (dwOrgAddr-dwAddr)==(dwOrgAddr&0x7F)
    for (i=0; i<wDataLength; i++)
    {
      pucDataBuf[(dwOrgAddr&0x7F)+wDataLength-1-i] = pucDataBuf[wDataLength-1-i];
    }

    //因为调整起始地址而补齐的数据从FLASH中读取，这里必须要先数据偏移然后才能读取
    for (i=0; i<(dwOrgAddr&0x7F); i++)
    {
      pucDataBuf[i] = M8(dwAddr + i);
    }
  }

  if ((dwOrgAddr&0x7F)||(wDataLength&0x7F))//起始地址/大小不是按照128字节对齐，需要调整
  {
    dwEndAddr = ((dwOrgAddr+wDataLength-1+0x7F) & ~0x7F) - 1;//按128字节对方方式调整得到结束地址

    //因为调整结束地址而补齐的数据从FLASH中读取
    for (i=((dwOrgAddr&0x7F)+wDataLength); i<(dwEndAddr - dwAddr + 1); i++)
    {
      pucDataBuf[i] = M8(dwAddr + i);
    }

    wDataLength = dwEndAddr - dwAddr + 1;//大小 = 结束地址-起始地址+1
  }
#else
  wDataLength = ucDataLength + 1;

  if ((wDataLength&0x7F)||(dwAddr&0x7F))
  {
    return RET_ERROR_NACK;
  }
#endif

  //1) 检查 FLASH_SR 寄存器 BSY 位，确认没有正在进行的 flash 操作

  //2) 如果没有正在进行的 flash erase 或者 program 操作，则软件读出该 Page 的 32 个
  //word（如果该 page 已有数据存放，则进行该步骤，否则跳过该步骤）

  //3) 向 FLASH_KEYR 寄存器依次写 KEY1 和 KEY2，解除 FLASH_CR 寄存器的保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH

  //4) 置位 FLASH_CR 寄存器的 PG 位
  SET_BIT(FLASH->CR, (FLASH_CR_PG|FLASH_CR_EOPIE));

  dwOffsetAddr = 0;
  //5) 向目标地址进行 program 数据的操作，只接受 32bit 的 program
  for (i=0; i<wDataLength/0x80; i++)
  {
#ifdef CHECK_WRP
    ucSector = (dwAddr+dwOffsetAddr-FLASH_BASE)/0x1000;
    if (0 == READ_BIT(FLASH->WRPR, (1U<<ucSector)))
    {
      eResultFlag = RET_ERROR_NACK;
      break;
    }
#endif
    for (j=0; j<0x20; j++)//0x20 = 32
    {
      M32(dwAddr+dwOffsetAddr) = M32(pucDataBuf+dwOffsetAddr);

      dwOffsetAddr += 4;

      if ((0x20-2) == j)//0x20 - 2 = 30
      {
        //6) 在软件写第 31 个 word 后， 置位 FLASH_CR 寄存器的 PGSTRT
        SET_BIT(FLASH->CR, FLASH_CR_PGSTRT);
      }
    }

    //7) 写第 32 个 word 后
    //8) 等待 FLASH_SR 寄存器的 BSY 位被清零
    while (FLASH_SR_BSY == READ_BIT(FLASH->SR, FLASH_SR_BSY));

    //9) 检查 FLASH_SR 寄存器的 EOP 标志位（当 program 操作已经成功，该位被置位），然后软件清零该位
    if (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP))
    {
      eResultFlag = RET_SUCCESS_ACK;
      //清零 EOP 标志, 写 1，清零该位。
      SET_BIT(FLASH->SR, FLASH_SR_EOP);
    }
    else
    {
      eResultFlag = RET_ERROR_NACK;
      break;
    }
  }

  //10) 如果不再有 program 操作，则软件清除 PG 位
  CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_PG));

  return eResultFlag;
}

/**
  * @brief  This function is used to write data in Option bytes.
  * @param  dwAddr The address where that data will be written.
  * @param  pucDataBuf The data to be written.
  * @param  ucDataLength The length of the data to be written.
  * @retval An ErrorStatus enumeration value:
  *          - RET_SUCCESS_ACK: Write Option bytes operation done
  *          - RET_ERROR_NACK:   Write Option bytes operation failed or the value of one parameter is not ok
  */
ErrorStatus WriteOption(uint32_t dwAddr, uint8_t* pucDataBuf, uint8_t ucDataLength)
{
  ErrorStatus eResultFlag;
  uint16_t i;
  uint32_t dwOPTR     = FLASH->OPTR;
  uint32_t dwWRPR     = FLASH->WRPR;
  uint32_t dwPCROPR0  = FLASH->PCROPR;

  //2) 检查 BSY 位，确认没有正在进行的 Flash 操作

  //1) 用之前描述的步骤，清零 OPTLOCK 位
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH
  if (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0x00U)
  {
    WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1);
    WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2);
  }

  for (i=0; i<ucDataLength+1; i++)
  {
    if ((OPTR1_BASE+0) == (dwAddr+i))//RDP
    {
      dwOPTR &= ~(0x000000FF);
      dwOPTR |= (pucDataBuf[i]<<0);
    }
    else if ((OPTR1_BASE+1) == (dwAddr+i))//BOR_EN,BOR_LEV[2:0],IWDG_SW,WWDG_SW,NRST_MODE,nBOOT1
    {
      dwOPTR &= ~(0x0000FF00);
      dwOPTR |= (pucDataBuf[i]<<8);
    }
    else if ((OPTR2_BASE+0) == (dwAddr+i))//BOR_EN,BOR_LEV[2:0],IWDG_SW,WWDG_SW,NRST_MODE,nBOOT1
    {
      dwOPTR &= ~(0x00FF0000);
      dwOPTR |= (pucDataBuf[i]<<16);
    }
    else if ((OPTR2_BASE+1) == (dwAddr+i))//BOR_EN,BOR_LEV[2:0],IWDG_SW,WWDG_SW,NRST_MODE,nBOOT1
    {
      dwOPTR &= ~(0xFF000000);
      dwOPTR |= (pucDataBuf[i]<<24);
    }

    if ((WRPR_BASE+0) == (dwAddr+i))//WRP[7:0]
    {
      dwWRPR &= ~(0x000000FF);
      dwWRPR |= (pucDataBuf[i]<<0);
    }
    else if ((WRPR_BASE+1) == (dwAddr+i))//WRP[15:8]
    {
      dwWRPR &= ~(0x0000FF00);
      dwWRPR |= (pucDataBuf[i]<<8);
    }


    if ((PCROP0SR_BASE+0) == (dwAddr+i))//PCROPR
    {
      dwPCROPR0 &= ~(0x000000FF);
      dwPCROPR0 |= (pucDataBuf[i]<<0);
    }
    else if ((PCROP0SR_BASE+1) == (dwAddr+i))
    {
      dwPCROPR0 &= ~(0x0000FF00);
      dwPCROPR0 |= (pucDataBuf[i]<<8);
    }
    else if ((PCROP0ER_BASE+0) == (dwAddr+i))
    {
      dwPCROPR0 &= ~(0x00FF0000);
      dwPCROPR0 |= (pucDataBuf[i]<<16);
    }
    else if ((PCROP0ER_BASE+1) == (dwAddr+i))
    {
      dwPCROPR0 &= ~(0xFF000000);
      dwPCROPR0 |= (pucDataBuf[i]<<24);
    }
  }

  //3) 向 option bytes 寄存器 FLASH_OPTR/FLASH_SDKR/FLASH_WRPR 写期望的值
  FLASH->OPTR    = dwOPTR;
  FLASH->WRPR    = dwWRPR;
  FLASH->PCROPR = dwPCROPR0;

  SET_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_OPTSTRT));
  M32(0x1FFF0F80) = 0xFF;

  //4) 等待 BSY 位被清零
  while (FLASH_SR_BSY == READ_BIT(FLASH->SR, FLASH_SR_BSY));

  //5) 等待 EOP 拉高，软件清零
  eResultFlag = (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP)) ? RET_SUCCESS_ACK : RET_ERROR_NACK;

  //清零 EOP 标志, 写 1，清零该位。
  SET_BIT(FLASH->SR, FLASH_SR_EOP);

  CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_OPTSTRT));

  //SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK);//FLASH_OB_Lock
  //如果软件置位 Lock 位，则 OPTLOCK 位也被自动置位

  if (RET_SUCCESS_ACK == eResultFlag)
  {
    USART_SendByte(ACK_BYTE);

    //当 FLASH_CR 寄存器中的 OBL_LAUNCH 位被置位, Option byte loading
    SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);
  }

  return eResultFlag;
}

/**
  * @brief  This function is used to start FLASH mass erase operation.
  * @retval An ErrorStatus enumeration value:
  *          - RET_SUCCESS_ACK: Mass erase operation done
  *          - RET_ERROR_NACK:   Mass erase operation failed or the value of one parameter is not ok
  */
ErrorStatus MassErase(void)
{
  ErrorStatus eResultFlag;

//  if (FLASH_WRPR_WRP != FLASH->WRPR)
//  {
//    return RET_ERROR_NACK;
//  }
//  if ((FLASH->SDKR&0x1F) <= ((FLASH->SDKR>>8)&0x1F))
//  {
//    return RET_ERROR_NACK;
//  }

  //1) 检查 BSY 位，确认是否没有正在进行的 Flash 操作

  //2) 向 FLASH_KEYR 寄存器依次写 KEY1,KEY2，解除 FLASH_CR 寄存器保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH

  //3) 置位 FLASH_CR 寄存器的 MER 位
  SET_BIT(FLASH->CR, (FLASH_CR_MER));

  //4) 向 flash 的任意空间写任意数据（32bit 数据）
  M32(FLASH_BASE) = 0xFFFFFFFF;

  //5) 等待 BSY 位被清零
  while (FLASH_SR_BSY == READ_BIT(FLASH->SR, FLASH_SR_BSY));

  //6) 检查 EOP 标志位被置位
  eResultFlag = (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP)) ? RET_SUCCESS_ACK : RET_ERROR_NACK;

  //7) 清零 EOP 标志, 写 1，清零该位。
  SET_BIT(FLASH->SR, FLASH_SR_EOP);

  CLEAR_BIT(FLASH->CR, (FLASH_CR_MER));

  return eResultFlag;
}

/**
  * @brief  This function is used to erase the specified FLASH pages.
  * @param  *pwDataBuf Pointer to the buffer that contains erase operation options.
  * @param  ucDataLength Size of the Data buffer.
  * @retval An ErrorStatus enumeration value:
  *          - RET_SUCCESS_ACK: Erase operation done
  *          - RET_ERROR_NACK:   Erase operation failed or the value of one parameter is not ok
  */
ErrorStatus PageErase(uint16_t* pwDataBuf, uint8_t ucDataLength, uint8_t ucPageCount)
{
  uint8_t i, j;
#ifdef CHECK_WRP
  uint8_t ucSector;
#endif
  uint32_t dwAddr;
  ErrorStatus eResultFlag = RET_SUCCESS_ACK;

  //1) 检查 BSY 位，确认是否没有正在进行的 Flash 操作

  //2) 向 FLASH_KEYR 寄存器依次写 KEY1,KEY2，解除 FLASH_CR 寄存器保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH

  //3) 置位 FLASH_CR 寄存器的 PER 位
  SET_BIT(FLASH->CR, (FLASH_CR_PER));

  for (i=0; i<ucDataLength+1; i++)
  {
    if (RET_SUCCESS_ACK != eResultFlag)
    {
      break;
    }
    for (j=0; j<ucPageCount; j++)
    {
      WDG_Refresh();

      dwAddr = FLASH_BASE+(pwDataBuf[i]*ucPageCount+j)*0x80;

//      if ((((M8(FLASHSIZE_BASE)&0x07)>>0)+1)*8*0x400 <= (dwAddr-FLASH_BASE))//检查容量
//      {
//        eResultFlag = RET_SUCCESS_ACK;
//        continue;
//      }

#ifdef CHECK_WRP
      ucSector = (dwAddr-FLASH_BASE)/0x1000;
      if (0 == READ_BIT(FLASH->WRPR, (1U<<ucSector)))
      {
        eResultFlag = RET_ERROR_NACK;
        break;
      }
#endif

      //向该 page 写任意数据（必须 32bit 数据）
      M32(dwAddr) = 0xFFFFFFFF;

      //5) 等待 BSY 位被清零
      while (FLASH_SR_BSY == READ_BIT(FLASH->SR, FLASH_SR_BSY));

      //6) 检查 EOP 标志位被置位
      if (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP))
      {
        eResultFlag = RET_SUCCESS_ACK;
        //7) 清零 EOP 标志, 写 1，清零该位。
        SET_BIT(FLASH->SR, FLASH_SR_EOP);
      }
      else
      {
        eResultFlag = RET_ERROR_NACK;
        break;
      }
    }
  }

  CLEAR_BIT(FLASH->CR, (FLASH_CR_PER));

  return eResultFlag;
}

/**
  * @brief  This function is used to erase the specified FLASH sectors.
  * @param  *pwDataBuf Pointer to the buffer that contains erase operation options.
  * @param  ucDataLength Size of the Data buffer.
  * @retval An ErrorStatus enumeration value:
  *          - RET_SUCCESS_ACK: Erase operation done
  *          - RET_ERROR_NACK:   Erase operation failed or the value of one parameter is not ok
  */
ErrorStatus SectorErase(uint16_t* pwDataBuf, uint8_t ucDataLength)
{
  uint8_t i;
  ErrorStatus eResultFlag;

  //1) 检查 BSY 位，确认是否没有正在进行的 Flash 操作

  //2) 向 FLASH_KEYR 寄存器依次写 KEY1,KEY2，解除 FLASH_CR 寄存器保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH

  //3) 置位 FLASH_CR 寄存器的 SER 位
  SET_BIT(FLASH->CR, (FLASH_CR_SER));

  for (i=0; i<ucDataLength+1; i++)
  {
    WDG_Refresh();

#ifdef CHECK_WRP
    if (0 == READ_BIT(FLASH->WRPR, (1U<<pwDataBuf[i])))
    {
      eResultFlag = RET_ERROR_NACK;
      break;
    }
#endif

    //4) 向该 sector 写任意数据
    M32(FLASH_BASE+pwDataBuf[i]*0x1000) = 0xFFFFFFFF;

    //5) 等待 BSY 位被清零
    while (FLASH_SR_BSY == READ_BIT(FLASH->SR, FLASH_SR_BSY));

    //6) 检查 EOP 标志位被置位
    if (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP))
    {
      eResultFlag = RET_SUCCESS_ACK;
      //7) 清零 EOP 标志, 写 1，清零该位。
      SET_BIT(FLASH->SR, FLASH_SR_EOP);
    }
    else
    {
      eResultFlag = RET_ERROR_NACK;
      break;
    }
  }

  CLEAR_BIT(FLASH->CR, (FLASH_CR_SER));

  return eResultFlag;
}
