
#include "Work_Flash.h"







/** Size of Work Flash sector in byte */
#define   WFLASH_SECTOR_SIZE                (4096U)

/** Size of Work Flash in byte */
#define   WFLASH_SIZE_BYTE                  (112UL * 1024UL)

/** Base address of WORK FLASH */
#define   WFLASH_BASE_ADDRESS               (0x0E000000UL)

#define   WFLASH_UNLOCK_KEY                 (0xCF6DF1A5UL)    /*! Unlock key code */

#define   WFLASH_COMMAND_ERASE              (2U)

/*** During blank check phase, if a certain number of unused records are found
     in the head of both sectors of a block, this block is considered blank */        
#define   WFLASH_BLANK_CHECK_RECORD_NUM     (3U)

#define   WFLASH_OVER_SEARCH_RECORD_NUM     (3U)






#define   WFLASH_MIN_BLOCK_SIZE             (WFLASH_BLOCK_CTRL_BYTE_SIZE)
#define   WFLASH_MAX_BLOCK_SIZE             (WFLASH_SECTOR_SIZE)

/*** Block size check ***/
#if  ((WFLASH_BLOCK_00_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_01_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_02_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_03_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_04_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_05_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_06_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_07_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_08_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_09_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_10_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_11_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_12_SIZE % 4U != 0U) || \
      (WFLASH_BLOCK_13_SIZE % 4U != 0U))
  #error "Block size must be a multiple of 4 bytes."
#endif

/*** Block 00 configure ***/
#if    WFLASH_BLOCK_00_SIZE == 0U
  #define WFLASH_BLOCK_00_ENABLE            (0U)
  #define WFLASH_BLOCK_00_BUFFER            (NULL)
#elif  WFLASH_BLOCK_00_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 00 is too small."
#elif  WFLASH_BLOCK_00_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock00Buffer[WFLASH_BLOCK_00_SIZE];

  #define WFLASH_BLOCK_00_ENABLE            (1U)
  #define WFLASH_BLOCK_00_BUFFER            (WFlashBlock00Buffer)
#else
  #error "Block 00 is too large."
#endif

/*** Block 01 configure ***/
#if    WFLASH_BLOCK_01_SIZE == 0U
  #define WFLASH_BLOCK_01_ENABLE            (0U)
  #define WFLASH_BLOCK_01_BUFFER            (NULL)
#elif  WFLASH_BLOCK_01_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 01 is too small."
#elif  WFLASH_BLOCK_01_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock01Buffer[WFLASH_BLOCK_01_SIZE];

  #define WFLASH_BLOCK_01_ENABLE            (1U)
  #define WFLASH_BLOCK_01_BUFFER            (WFlashBlock01Buffer)
#else
  #error "Block 01 is too large."
#endif

/*** Block 02 configure ***/
#if    WFLASH_BLOCK_02_SIZE == 0U
  #define WFLASH_BLOCK_02_ENABLE            (0U)
  #define WFLASH_BLOCK_02_BUFFER            (NULL)
#elif  WFLASH_BLOCK_02_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 02 is too small."
#elif  WFLASH_BLOCK_02_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock02Buffer[WFLASH_BLOCK_02_SIZE];

  #define WFLASH_BLOCK_02_ENABLE            (1U)
  #define WFLASH_BLOCK_02_BUFFER            (WFlashBlock02Buffer)
#else
  #error "Block 02 is too large."
#endif

/*** Block 03 configure ***/
#if    WFLASH_BLOCK_03_SIZE == 0U
  #define WFLASH_BLOCK_03_ENABLE            (0U)
  #define WFLASH_BLOCK_03_BUFFER            (NULL)
#elif  WFLASH_BLOCK_03_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 03 is too small."
#elif  WFLASH_BLOCK_03_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock03Buffer[WFLASH_BLOCK_03_SIZE];

  #define WFLASH_BLOCK_03_ENABLE            (1U)
  #define WFLASH_BLOCK_03_BUFFER            (WFlashBlock03Buffer)
#else
  #error "Block 03 is too large."
#endif

/*** Block 04 configure ***/
#if    WFLASH_BLOCK_04_SIZE == 0U
  #define WFLASH_BLOCK_04_ENABLE            (0U)
  #define WFLASH_BLOCK_04_BUFFER            (NULL)
#elif  WFLASH_BLOCK_04_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 04 is too small."
#elif  WFLASH_BLOCK_04_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock04Buffer[WFLASH_BLOCK_04_SIZE];

  #define WFLASH_BLOCK_04_ENABLE            (1U)
  #define WFLASH_BLOCK_04_BUFFER            (WFlashBlock04Buffer)
#else
  #error "Block 04 is too large."
#endif

/*** Block 05 configure ***/
#if    WFLASH_BLOCK_05_SIZE == 0U
  #define WFLASH_BLOCK_05_ENABLE            (0U)
  #define WFLASH_BLOCK_05_BUFFER            (NULL)
#elif  WFLASH_BLOCK_05_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 05 is too small."
#elif  WFLASH_BLOCK_05_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock05Buffer[WFLASH_BLOCK_05_SIZE];

  #define WFLASH_BLOCK_05_ENABLE            (1U)
  #define WFLASH_BLOCK_05_BUFFER            (WFlashBlock05Buffer)
#else
  #error "Block 05 is too large."
#endif

/*** Block 06 configure ***/
#if    WFLASH_BLOCK_06_SIZE == 0U
  #define WFLASH_BLOCK_06_ENABLE            (0U)
  #define WFLASH_BLOCK_06_BUFFER            (NULL)
#elif  WFLASH_BLOCK_06_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 06 is too small."
#elif  WFLASH_BLOCK_06_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock06Buffer[WFLASH_BLOCK_06_SIZE];

  #define WFLASH_BLOCK_06_ENABLE            (1U)
  #define WFLASH_BLOCK_06_BUFFER            (WFlashBlock06Buffer)
#else
  #error "Block 06 is too large."
#endif

/*** Block 07 configure ***/
#if    WFLASH_BLOCK_07_SIZE == 0U
  #define WFLASH_BLOCK_07_ENABLE            (0U)
  #define WFLASH_BLOCK_07_BUFFER            (NULL)
#elif  WFLASH_BLOCK_07_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 07 is too small."
#elif  WFLASH_BLOCK_07_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock07Buffer[WFLASH_BLOCK_07_SIZE];

  #define WFLASH_BLOCK_07_ENABLE            (1U)
  #define WFLASH_BLOCK_07_BUFFER            (WFlashBlock07Buffer)
#else
  #error "Block 07 is too large."
#endif

/*** Block 08 configure ***/
#if    WFLASH_BLOCK_08_SIZE == 0U
  #define WFLASH_BLOCK_08_ENABLE            (0U)
  #define WFLASH_BLOCK_08_BUFFER            (NULL)
#elif  WFLASH_BLOCK_08_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 08 is too small."
#elif  WFLASH_BLOCK_08_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock08Buffer[WFLASH_BLOCK_08_SIZE];

  #define WFLASH_BLOCK_08_ENABLE            (1U)
  #define WFLASH_BLOCK_08_BUFFER            (WFlashBlock08Buffer)
#else
  #error "Block 08 is too large."
#endif

/*** Block 09 configure ***/
#if    WFLASH_BLOCK_09_SIZE == 0U
  #define WFLASH_BLOCK_09_ENABLE            (0U)
  #define WFLASH_BLOCK_09_BUFFER            (NULL)
#elif  WFLASH_BLOCK_09_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 09 is too small."
#elif  WFLASH_BLOCK_09_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock09Buffer[WFLASH_BLOCK_09_SIZE];

  #define WFLASH_BLOCK_09_ENABLE            (1U)
  #define WFLASH_BLOCK_09_BUFFER            (WFlashBlock09Buffer)
#else
  #error "Block 09 is too large."
#endif

/*** Block 10 configure ***/
#if    WFLASH_BLOCK_10_SIZE == 0U
  #define WFLASH_BLOCK_10_ENABLE            (0U)
  #define WFLASH_BLOCK_10_BUFFER            (NULL)
#elif  WFLASH_BLOCK_10_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 10 is too small."
#elif  WFLASH_BLOCK_10_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock10Buffer[WFLASH_BLOCK_10_SIZE];

  #define WFLASH_BLOCK_10_ENABLE            (1U)
  #define WFLASH_BLOCK_10_BUFFER            (WFlashBlock10Buffer)
#else
  #error "Block 10 is too large."
#endif

/*** Block 11 configure ***/
#if    WFLASH_BLOCK_11_SIZE == 0U
  #define WFLASH_BLOCK_11_ENABLE            (0U)
  #define WFLASH_BLOCK_11_BUFFER            (NULL)
#elif  WFLASH_BLOCK_11_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 11 is too small."
#elif  WFLASH_BLOCK_11_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock11Buffer[WFLASH_BLOCK_11_SIZE];

  #define WFLASH_BLOCK_11_ENABLE            (1U)
  #define WFLASH_BLOCK_11_BUFFER            (WFlashBlock11Buffer)
#else
  #error "Block 11 is too large."
#endif

/*** Block 12 configure ***/
#if    WFLASH_BLOCK_12_SIZE == 0U
  #define WFLASH_BLOCK_12_ENABLE            (0U)
  #define WFLASH_BLOCK_12_BUFFER            (NULL)
#elif  WFLASH_BLOCK_12_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 12 is too small."
#elif  WFLASH_BLOCK_12_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock12Buffer[WFLASH_BLOCK_12_SIZE];

  #define WFLASH_BLOCK_12_ENABLE            (1U)
  #define WFLASH_BLOCK_12_BUFFER            (WFlashBlock12Buffer)
#else
  #error "Block 12 is too large."
#endif

/*** Block 13 configure ***/
#if    WFLASH_BLOCK_13_SIZE == 0U
  #define WFLASH_BLOCK_13_ENABLE            (0U)
  #define WFLASH_BLOCK_13_BUFFER            (NULL)
#elif  WFLASH_BLOCK_13_SIZE <  WFLASH_MIN_BLOCK_SIZE
  #error "Block 13 is too small."
#elif  WFLASH_BLOCK_13_SIZE <= WFLASH_MAX_BLOCK_SIZE
  #pragma data_alignment = 4
  uint32_t  WFlashBlock13Buffer[WFLASH_BLOCK_13_SIZE];

  #define WFLASH_BLOCK_13_ENABLE            (1U)
  #define WFLASH_BLOCK_13_BUFFER            (WFlashBlock13Buffer)
#else
  #error "Block 13 is too large."
#endif

typedef union
{
  uint32_t  u32Word;
  
  struct
  {
    uint16_t  u16Index;
    uint16_t  u16IndexInv;
  }stField;
}WFlash_Ctrl_Data_un_t;

typedef struct
{
  WFlash_Block_Status_en_t  enStatus;
  uint8_t                   u8SectorOffset;
  uint16_t                  u16Index;
  uint16_t                  u16RecordNum;
  uint16_t                  u16MaxRecordNum;
  uint16_t                  u16BlockSize;
  uint32_t*                 pu32Data;
}WFlash_RW_Ctrl_st_t;

typedef struct
{
  uint8_t    u8Busy;
  uint16_t   u16Cnt;
  uint16_t   u16Total;
  uint32_t   u32DstAddr;
  uint32_t*  pu32DataBuffer;
}WFlash_Wr_Ctrl_st_t;

typedef struct
{
  uint8_t    u8Enable;
  uint8_t    u8Rsvd;
  uint16_t   u16Size;
  uint32_t*  pu32Buffer;
}WFlash_Block_st_t;

static const WFlash_Block_st_t WFlashBlock[WFLASH_BLOCK_NUMBER] =
{
/*================================================================================
        u8Enable        | u8Rsvd |       u16Size       |       pu32Buffer
--------------------------------------------------------------------------------*/
{ WFLASH_BLOCK_00_ENABLE,       0, WFLASH_BLOCK_00_SIZE, WFLASH_BLOCK_00_BUFFER,},
{ WFLASH_BLOCK_01_ENABLE,       0, WFLASH_BLOCK_01_SIZE, WFLASH_BLOCK_01_BUFFER,},
{ WFLASH_BLOCK_02_ENABLE,       0, WFLASH_BLOCK_02_SIZE, WFLASH_BLOCK_02_BUFFER,},
{ WFLASH_BLOCK_03_ENABLE,       0, WFLASH_BLOCK_03_SIZE, WFLASH_BLOCK_03_BUFFER,},
{ WFLASH_BLOCK_04_ENABLE,       0, WFLASH_BLOCK_04_SIZE, WFLASH_BLOCK_04_BUFFER,},
{ WFLASH_BLOCK_05_ENABLE,       0, WFLASH_BLOCK_05_SIZE, WFLASH_BLOCK_05_BUFFER,},
{ WFLASH_BLOCK_06_ENABLE,       0, WFLASH_BLOCK_06_SIZE, WFLASH_BLOCK_06_BUFFER,},
{ WFLASH_BLOCK_07_ENABLE,       0, WFLASH_BLOCK_07_SIZE, WFLASH_BLOCK_07_BUFFER,},
{ WFLASH_BLOCK_08_ENABLE,       0, WFLASH_BLOCK_08_SIZE, WFLASH_BLOCK_08_BUFFER,},
{ WFLASH_BLOCK_09_ENABLE,       0, WFLASH_BLOCK_09_SIZE, WFLASH_BLOCK_09_BUFFER,},
{ WFLASH_BLOCK_10_ENABLE,       0, WFLASH_BLOCK_10_SIZE, WFLASH_BLOCK_10_BUFFER,},
{ WFLASH_BLOCK_11_ENABLE,       0, WFLASH_BLOCK_11_SIZE, WFLASH_BLOCK_11_BUFFER,},
{ WFLASH_BLOCK_12_ENABLE,       0, WFLASH_BLOCK_12_SIZE, WFLASH_BLOCK_12_BUFFER,},
{ WFLASH_BLOCK_13_ENABLE,       0, WFLASH_BLOCK_13_SIZE, WFLASH_BLOCK_13_BUFFER,},
/*==============================================================================*/
};


WFlash_RW_Ctrl_st_t stWFlashRWCtrl[WFLASH_BLOCK_NUMBER];
WFlash_Wr_Ctrl_st_t stWFlashWrCtrl;

static __irq __arm void WFlash_Ready_ISR(void);


void WFlash_Controller_Init(void)
{
  stWFlashWrCtrl.u8Busy         = 0U;
  stWFlashWrCtrl.u16Cnt         = 0U;
  stWFlashWrCtrl.u16Total       = 0U;
  stWFlashWrCtrl.u32DstAddr     = 0x00000000UL;
  stWFlashWrCtrl.pu32DataBuffer = NULL;
  
  /* Register ISR */
  Interrupt_IRQ_ISR_Register(INTERRUPTS_IRQ_NUMBER_20, (uint32_t)WFlash_Ready_ISR, 31U);
}


WFlash_Status_en_t WFlash_Get_Controller_Status(void)
{
  return (WFlash_Status_en_t)stWFlashWrCtrl.u8Busy;
}

void WFlash_Block_Init(void)
{
  uint16_t  i;
    
  uint8_t   u8Loop;
  uint8_t   u8Ready;
  uint8_t   u8Continue;
  uint8_t   u8BlockNum;
  uint16_t  u16Index;
  uint16_t  u16IndexBkup;
  uint16_t  u16WordNum;
  uint16_t  u16RecordNum;
  uint32_t  u32DataAddr;
  uint32_t  u32DataAddrBase;
  
  WFlash_Ctrl_Data_un_t  unCtrlData;
  
  /*** Step 1 : Generate flash read / write control data ***/
  u8Continue = 0U;
  for (u8BlockNum = 0U; u8BlockNum < WFLASH_BLOCK_NUMBER; u8BlockNum++)
  {
    stWFlashRWCtrl[u8BlockNum].enStatus = (WFlash_Block_Status_en_t)WFlashBlock[u8BlockNum].u8Enable;
    
    if (stWFlashRWCtrl[u8BlockNum].enStatus)
    {
      u8Continue = 1U;
      
      stWFlashRWCtrl[u8BlockNum].u8SectorOffset  = 0U;
      stWFlashRWCtrl[u8BlockNum].u16Index        = 0U;
      stWFlashRWCtrl[u8BlockNum].u16RecordNum    = 0U;
      stWFlashRWCtrl[u8BlockNum].u16BlockSize    = WFlashBlock[u8BlockNum].u16Size;
      stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum = WFLASH_SECTOR_SIZE / stWFlashRWCtrl[u8BlockNum].u16BlockSize;
      stWFlashRWCtrl[u8BlockNum].pu32Data        = WFlashBlock[u8BlockNum].pu32Buffer;
    }
  }
  
  /*** Step 2 : Blank check and find start record ***/
  if (u8Continue)
  {
    for (u8BlockNum = 0U; u8BlockNum < WFLASH_BLOCK_NUMBER; u8BlockNum++)
    {
      if (stWFlashRWCtrl[u8BlockNum].enStatus)
      {
        /* Just make .enStatus clearer */
        stWFlashRWCtrl[u8BlockNum].enStatus = WFLASH_STAT_BLANK;
        
        /* Search sector +0 */
        u32DataAddrBase   = WFLASH_BASE_ADDRESS + WFLASH_SECTOR_SIZE * 2U * (uint32_t)u8BlockNum;
        u16RecordNum      = 0U;
        u8Ready           = 0U;
        u8Loop            = 1U;
        
        while (u8Loop)
        {
          /* Fetch block control data */
          u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)stWFlashRWCtrl[u8BlockNum].u16BlockSize;
          unCtrlData.u32Word = *((volatile uint32_t*)u32DataAddr);
          
          if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
          {
            u8Loop     = 0U;
            u8Ready    = 1U;
            u8Continue = 1U;
            
            stWFlashRWCtrl[u8BlockNum].enStatus        = WFLASH_STAT_ACTIVE;
            stWFlashRWCtrl[u8BlockNum].u8SectorOffset  = 0U;
            stWFlashRWCtrl[u8BlockNum].u16RecordNum    = u16RecordNum;
            stWFlashRWCtrl[u8BlockNum].u16Index        = unCtrlData.stField.u16Index;
            u16IndexBkup                               = unCtrlData.stField.u16Index;
          }
          else
          {
            u16RecordNum++;
            if ((u16RecordNum >= stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum) || \
                (u16RecordNum >= WFLASH_BLANK_CHECK_RECORD_NUM))
            {
              u8Loop = 0U;
            }
          }
        }
        
        /* Search sector +1 */
        u32DataAddrBase  += WFLASH_SECTOR_SIZE;
        u16RecordNum      = 0U;
        u8Loop            = 1U;
        
        while (u8Loop)
        {
          /* Fetch block control data */
          u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)stWFlashRWCtrl[u8BlockNum].u16BlockSize;
          unCtrlData.u32Word = *((volatile uint32_t*)u32DataAddr);
          
          if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
          {
            u8Loop     = 0U;
            u8Continue = 1U;
            
            if (u8Ready == 0U)
            {
              u8Ready    = 1U;
              stWFlashRWCtrl[u8BlockNum].enStatus        = WFLASH_STAT_ACTIVE;
              stWFlashRWCtrl[u8BlockNum].u8SectorOffset  = 1U;
              stWFlashRWCtrl[u8BlockNum].u16RecordNum    = u16RecordNum;
              stWFlashRWCtrl[u8BlockNum].u16Index        = unCtrlData.stField.u16Index;
              u16IndexBkup                               = unCtrlData.stField.u16Index;
            }
            else
            {
              if (unCtrlData.stField.u16Index >= u16IndexBkup)
              {
                u16Index = unCtrlData.stField.u16Index - u16IndexBkup;
              }
              else
              {
                u16Index = unCtrlData.stField.u16Index + (0xFFFFU - u16IndexBkup) + 1U;
              }
              
              if (u16Index < stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum + WFLASH_BLANK_CHECK_RECORD_NUM)
              {
                stWFlashRWCtrl[u8BlockNum].u8SectorOffset  = 1U;
                stWFlashRWCtrl[u8BlockNum].u16RecordNum    = u16RecordNum;
                stWFlashRWCtrl[u8BlockNum].u16Index        = unCtrlData.stField.u16Index;
              }
            }
          }
          else
          {
            u16RecordNum++;
            if ((u16RecordNum >= stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum) || \
                (u16RecordNum >= WFLASH_BLANK_CHECK_RECORD_NUM))
            {
              u8Loop = 0U;
            }
          }
        }
      }
    }
  }
  
  /*** Step 3 : Search for the latest record ***/
  if (u8Continue)
  {
    for (u8BlockNum = 0U; u8BlockNum < WFLASH_BLOCK_NUMBER; u8BlockNum++)
    {
      if (stWFlashRWCtrl[u8BlockNum].enStatus == WFLASH_STAT_ACTIVE)
      {
        /* Find the last record by index */
        u32DataAddrBase = WFLASH_BASE_ADDRESS + WFLASH_SECTOR_SIZE * 2U * (uint32_t)u8BlockNum + \
                          WFLASH_SECTOR_SIZE * (uint32_t)stWFlashRWCtrl[u8BlockNum].u8SectorOffset;
        u16Index        = stWFlashRWCtrl[u8BlockNum].u16Index;
        u16RecordNum    = stWFlashRWCtrl[u8BlockNum].u16RecordNum;
        u8Loop          = 1U;
        
        while (u8Loop)
        {
          u16RecordNum++;
          u16Index++;
          if ((u16RecordNum >= stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum) || \
              (u16RecordNum  - stWFlashRWCtrl[u8BlockNum].u16RecordNum >= WFLASH_OVER_SEARCH_RECORD_NUM))
          {
            u8Loop = 0U;
          }
          else
          {
            /* Fetch block control data */
            u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)stWFlashRWCtrl[u8BlockNum].u16BlockSize;
            unCtrlData.u32Word = *((volatile uint32_t*)u32DataAddr);
          
            if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
            {
              if (unCtrlData.stField.u16Index == u16Index)
              {
                stWFlashRWCtrl[u8BlockNum].u16RecordNum = u16RecordNum;
                stWFlashRWCtrl[u8BlockNum].u16Index     = unCtrlData.stField.u16Index;
              }
            }
          }
        }
        
        /* Load data */
        u16RecordNum    = stWFlashRWCtrl[u8BlockNum].u16RecordNum;
        u16Index        = stWFlashRWCtrl[u8BlockNum].u16Index;
        u16IndexBkup    = u16Index;
        u8Loop = 1U;
        do
        {
          CRC_Init(CRC16, CRC_Little_Endian, CRC_Little_Endian);
          u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)stWFlashRWCtrl[u8BlockNum].u16BlockSize;
          unCtrlData.u32Word = *((volatile uint32_t*)u32DataAddr);
          
          if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
          {
            if (u16Index == unCtrlData.stField.u16Index)
            {
              u16IndexBkup = u16Index;
              u16WordNum   = stWFlashRWCtrl[u8BlockNum].u16BlockSize / 4U - 1U;
              
              /* Load header and data but not CRC checksum */
              for (i = 0U; i < u16WordNum; i++)
              {
                stWFlashRWCtrl[u8BlockNum].pu32Data[i] = *((volatile uint32_t*)u32DataAddr);
                CRC_Write(stWFlashRWCtrl[u8BlockNum].pu32Data[i]);
                
                u32DataAddr += 4UL;
              }
              
              /* Load CRC checksum separately */
              stWFlashRWCtrl[u8BlockNum].pu32Data[i] = *((volatile uint32_t*)u32DataAddr);
              if (stWFlashRWCtrl[u8BlockNum].pu32Data[i] == CRC_Read())
              {
                u8Loop = 0U;
              }
            }
          }
          
          if (u8Loop)     /* CRC check fail */
          {
            u16Index--;   /* Go to previous record */
            
            if (u16IndexBkup - u16Index <= WFLASH_OVER_SEARCH_RECORD_NUM)
            {
              if (u16RecordNum == 0U)   /* Cross sector access */
              {
                u16RecordNum     = stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum - 1U;
                
                if (stWFlashRWCtrl[u8BlockNum].u8SectorOffset)
                {
                  u32DataAddrBase -= WFLASH_SECTOR_SIZE;
                }
                else
                {
                  u32DataAddrBase += WFLASH_SECTOR_SIZE;
                }
              }
              else
              {
                u16RecordNum--;
              }
            }
            else
            {
              /* No correct data found, set error flag */
              u8Loop = 0U;
              stWFlashRWCtrl[u8BlockNum].enStatus = WFLASH_STAT_ERROR;
            }
          }
        }while(u8Loop);
      }
    }
  }
}

WFlash_Block_Status_en_t WFlash_Get_Block_Status(uint8_t u8BlockNum)
{
  WFlash_Block_Status_en_t enStatus;
  
  if (u8BlockNum < WFLASH_BLOCK_NUMBER)
  {
    enStatus = stWFlashRWCtrl[u8BlockNum].enStatus;
  }
  else
  {
    enStatus = WFLASH_STAT_DISABLE;
  }
  
  return enStatus;
}

WFlash_RW_Result_en_t WFlash_Read_Block_Data(uint8_t u8BlockNum, uint32_t u32Data[], uint16_t u16Len)
{
  uint16_t  i;
  WFlash_RW_Result_en_t enResult;
  
  if ((u8BlockNum < WFLASH_BLOCK_NUMBER) && (u32Data != NULL) && (u16Len != 0U))
  {
    if ((u16Len <= (stWFlashRWCtrl[u8BlockNum].u16BlockSize - WFLASH_BLOCK_CTRL_BYTE_SIZE) / 4U) && \
        (stWFlashRWCtrl[u8BlockNum].enStatus == WFLASH_STAT_ACTIVE))
    {
      for (i = 0U; i < u16Len; i++)
      {
        u32Data[i] = stWFlashRWCtrl[u8BlockNum].pu32Data[i + 1U];
      }
      
      enResult = WFLASH_RW_PASS;
    }
    else
    {
      enResult = WFLASH_RW_FAIL;
    }
  }
  else
  {
    enResult = WFLASH_RW_FAIL;
  }
  
  return enResult;
}

WFlash_RW_Result_en_t WFlash_Write_Block_Data(uint8_t u8BlockNum, uint32_t u32Data[], uint16_t u16Len)
{
  uint16_t  i;
  
  uint8_t   u8Erase;
  uint16_t  u16WordNum;
  uint32_t  u32DstAddr;

  WFlash_Ctrl_Data_un_t unCtrlData;
  un_wfcfg_seqcm_t      unSEQCM;
  WFlash_RW_Result_en_t enResult;
  
  if ((u8BlockNum < WFLASH_BLOCK_NUMBER) && (u32Data != NULL) && (u16Len != 0U) && (stWFlashWrCtrl.u8Busy == 0U))
  {
    if ((u16Len <= (stWFlashRWCtrl[u8BlockNum].u16BlockSize - WFLASH_BLOCK_CTRL_BYTE_SIZE) / 4U) && \
        (stWFlashRWCtrl[u8BlockNum].enStatus != WFLASH_STAT_DISABLE))
    {
      u8Erase    = 0U;
      u16WordNum = stWFlashRWCtrl[u8BlockNum].u16BlockSize / 4U - 1U;     /* CRC checksum is ignored */
      
      CRC_Init(CRC16, CRC_Little_Endian, CRC_Little_Endian);
      
      if (stWFlashRWCtrl[u8BlockNum].enStatus == WFLASH_STAT_ACTIVE)
      {
        stWFlashRWCtrl[u8BlockNum].u16RecordNum++;
        if (stWFlashRWCtrl[u8BlockNum].u16RecordNum >= stWFlashRWCtrl[u8BlockNum].u16MaxRecordNum)
        {
          u8Erase = 1U;
          stWFlashRWCtrl[u8BlockNum].u16RecordNum = 0U;
          if (stWFlashRWCtrl[u8BlockNum].u8SectorOffset == 0U)
          {
            stWFlashRWCtrl[u8BlockNum].u8SectorOffset = 1U;
          }
          else
          {
            stWFlashRWCtrl[u8BlockNum].u8SectorOffset = 0U;
          }
        }
        
        stWFlashRWCtrl[u8BlockNum].u16Index++;
        unCtrlData.stField.u16Index = stWFlashRWCtrl[u8BlockNum].u16Index;
        unCtrlData.stField.u16IndexInv = 0xFFFFU - unCtrlData.stField.u16Index;
        stWFlashRWCtrl[u8BlockNum].pu32Data[0] = unCtrlData.u32Word;
      }
      else
      {
        u8Erase = 1U;
        stWFlashRWCtrl[u8BlockNum].u16RecordNum    = 0U;
        stWFlashRWCtrl[u8BlockNum].u8SectorOffset  = 0U;
        stWFlashRWCtrl[u8BlockNum].u16Index        = 0U;
        stWFlashRWCtrl[u8BlockNum].pu32Data[0]     = 0xFFFF0000UL;
        
        for (i = 1U; i < u16WordNum; i++)
        {
          stWFlashRWCtrl[u8BlockNum].pu32Data[i] = 0x00000000UL;
        }
      }
      
      CRC_Write(stWFlashRWCtrl[u8BlockNum].pu32Data[0]);
      
      u16Len += 1U;
      for (i = 1U; i < u16Len; i++)
      {
        stWFlashRWCtrl[u8BlockNum].pu32Data[i] = u32Data[i - 1U];
        CRC_Write(stWFlashRWCtrl[u8BlockNum].pu32Data[i]);
      }
      
      for (; i < u16WordNum; i++)
      {
        CRC_Write(stWFlashRWCtrl[u8BlockNum].pu32Data[i]);
      }
      
      stWFlashRWCtrl[u8BlockNum].pu32Data[i] = CRC_Read();
      
      u32DstAddr  = WFLASH_BASE_ADDRESS + WFLASH_SECTOR_SIZE * 2U * (uint32_t)u8BlockNum;
      u32DstAddr += WFLASH_SECTOR_SIZE * (uint32_t)stWFlashRWCtrl[u8BlockNum].u8SectorOffset;
      u32DstAddr += (uint32_t)stWFlashRWCtrl[u8BlockNum].u16BlockSize * (uint32_t)stWFlashRWCtrl[u8BlockNum].u16RecordNum;
      
      IRQ_DISABLE_LOCAL()  
      if (WFCFG_WARBR_WERSTS != 0U)     /* Check if can get the write permission */
      {
        WFCFG_CPR   = WFLASH_UNLOCK_KEY;  /* Unlock work flash controller */
        WFCFG_CR_WE = 1U;                 /* Work Flash write enable */
        enResult    = WFLASH_RW_PASS;
      }
      else
      {
        enResult   = WFLASH_RW_FAIL;
      }
      IRQ_RESTORE()
      
      if (WFCFG_SR_RDY == 0U)
      {
        enResult   = WFLASH_RW_FAIL;
      }        
      
      if (enResult == WFLASH_RW_PASS)
      {
        stWFlashWrCtrl.u8Busy         = 1U;
        stWFlashWrCtrl.u16Total       = stWFlashRWCtrl[u8BlockNum].u16BlockSize / 4U;
        stWFlashWrCtrl.pu32DataBuffer = stWFlashRWCtrl[u8BlockNum].pu32Data;
        
        WFCFG_ICR_RDYIC = 1U;
        WFCFG_ICR_RDYIE = 1U;
        
        if (u8Erase)
        {
          stWFlashWrCtrl.u16Cnt     = 0U;
          stWFlashWrCtrl.u32DstAddr = u32DstAddr;
          
          /* Write erase command. */
          unSEQCM.u32Register    = 0x00000000UL;
          unSEQCM.stcField.u4OPC = WFLASH_COMMAND_ERASE;
          unSEQCM.stcField.u8ERS = u8BlockNum * 2U + stWFlashRWCtrl[u8BlockNum].u8SectorOffset;
          WFCFG_SEQCM            = unSEQCM.u32Register;
        }
        else
        {
          stWFlashWrCtrl.u16Cnt     = 1U;
          stWFlashWrCtrl.u32DstAddr = u32DstAddr + 4UL;
          
          *((volatile uint32_t *)u32DstAddr) = stWFlashWrCtrl.pu32DataBuffer[0];
        }
        
        stWFlashRWCtrl[u8BlockNum].enStatus = WFLASH_STAT_ACTIVE;
      }      
    }
    else
    {
      enResult = WFLASH_RW_FAIL;
    }
  }
  else
  {
    enResult = WFLASH_RW_FAIL;
  }
  
  return enResult;
}

static __irq __arm void WFlash_Ready_ISR(void)
{
  WFCFG_ICR_RDYIC = 1U;
  
  if (stWFlashWrCtrl.u16Cnt < stWFlashWrCtrl.u16Total)
  {
    *((volatile uint32_t *)stWFlashWrCtrl.u32DstAddr) = stWFlashWrCtrl.pu32DataBuffer[stWFlashWrCtrl.u16Cnt];
    
    stWFlashWrCtrl.u16Cnt     += 1U;
    stWFlashWrCtrl.u32DstAddr += 4UL;
  }
  else
  {
    stWFlashWrCtrl.u8Busy  = 0U;
    
    WFCFG_ICR_RDYIE = 0U;
    
    IRQ_DISABLE_LOCAL()
    
    WFCFG_CPR   = WFLASH_UNLOCK_KEY;  /* Unlock work flash controller */
    WFCFG_CR_WE = 0U;                 /* Work Flash write disable */
    
    IRQ_RESTORE()
  }
  
  IRC0_IRQHC = INTERRUPTS_IRQ_NUMBER_20;
}
