text: /**
    *********************************************************************************
    *
    * @file    ald_flash_ext.c
    * @brief   FLASH module driver.
    *
    * @version V1.0
    * @date    15 May 2019
    * @author  AE Team
    * @note
    *
    * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
    *
    *********************************************************************************
    *
    *********************************************************************************
    * @verbatim
    ==============================================================================
                      ##### FLASH Peripheral features #####
    ==============================================================================
    [..]
    Base address is 0x00000000
  
    [..]
    FLASH have just one programme mode , word programme.
    word programme can programme 8 bytes once ;
  
    ==============================================================================
                          ##### How to use this driver #####
    ==============================================================================
    [..]
     (#) programme flash using ald_flash_write(uint32_t addr, uint8_t *buf, uint16_t len)
         (++) call the function and supply all the three paraments is needs, addr means
              the first address to write in this operation, buf is a pointer to the data which
  	    need writing to flash.
  
     (#) erase flash using ald_flash_erase(uint32_t addr, uint16_t len)
         (++) call the function and supply two paraments, addr is the first address to erase,
              len is the length to erase
  
     (#) read flash using ald_flash_read(uint32_t *ram_addr, uint32_t addr, uint16_t len)
         (++) read the flash and save to a buffer, ram_addr is the buffer's first address,
              addr is the start reading address in flash, len is the length need read
  
      @endverbatim
    */
  
  
  #include "ald_flash.h"
  
  
  /** @addtogroup ES32FXXX_ALD
    * @{
    */
  
  /** @addtogroup FLASH
    * @{
    */
  
  #ifdef ALD_FLASH
  
  /** @addtogroup Flash_Private_Variables
    * @{
    */
  /* opration buffer*/
  static uint8_t write_buf[FLASH_PAGE_SIZE];
  /**
    * @}
    */
  
  /** @addtogroup Flash_Private_Functions
    * @{
    */
  
  /**
    * @brief  Check whether the flash between the given address section
    *         have been writen, if it have been writen, return TRUE, else
    *         return FALSE.
    * @param  begin_addr: The begin address.
    * @param  end_addr: The end address.
    * @retval The check result
    *         - TRUE
    *         - FALSE
    */
  static type_bool_t page_have_writen(uint32_t begin_addr, uint32_t end_addr)
  {
  	uint8_t* addr_to_read;
  	uint8_t value;
  	uint32_t index;
  
  	/* Check the parameters */
  	assert_param(IS_FLASH_ADDRESS(begin_addr));
  	assert_param(IS_FLASH_ADDRESS(end_addr));
  
  	addr_to_read = (uint8_t *)begin_addr;
  	index        = begin_addr;
  	value        = 0xFF;
  
  	if (begin_addr > end_addr)
  		return FALSE;
  
  	while (index++ <= end_addr) {
  		value = *addr_to_read++;
  
  		if (value != 0xFF)
  			break;
  	}
  
  	return value == 0xFF ? FALSE : TRUE;
  }
  /**
    * @}
    */
  
  /** @defgroup Flash_Public_Functions Flash Public Functions
    * @verbatim
   ===============================================================================
                ##### Flash operation functions #####
   ===============================================================================
    [..]
      This section provides functions allowing to operate flash, such as read and write.
  
      @endverbatim
    * @{
    */
  
  /**
    * @brief  read the specified length bytes from flash, and store to the specified area.
    * @param  ram_addr: the specified area to store the reading bytes.
    * @param  addr: the start address.
    * @param  len: the length to read.
    * @retval Status, see @ref ald_status_t.
    */
  ald_status_t ald_flash_read(uint32_t *ram_addr, uint32_t addr, uint16_t len)
  {
  	uint32_t i;
  	uint32_t temp;
  
  	assert_param(IS_4BYTES_ALIGN(ram_addr));
  	assert_param(IS_FLASH_ADDRESS(addr));
  	assert_param(IS_FLASH_ADDRESS(addr + len - 1));
  
  	temp = (uint32_t)ram_addr;
  
  	if (((temp & 0x3) != 0) || (((addr) & 0x3) != 0))
  		return ERROR;
  
  	for (i = 0; i < len; i++) {
  		ram_addr[i] = ((uint32_t *)addr)[i];
  	}
  
  	return OK;
  }
  
  /**
    * @brief  Write the give bytes to the given address section.
    * @param  addr: The start address to write.
    * @param  buf: The bytes' address.
    * @param  len: The length to write,and multiple of 2.
    * @retval Status, see @ref ald_status_t.
    */
  
  ald_status_t ald_flash_write(uint32_t addr, uint8_t *buf, uint16_t len)
  {
  	uint32_t index = 0;
  	uint32_t para = 0;
  	uint32_t index2 = 0;
  	uint32_t start_write_addr;
  	uint32_t end_write_addr;
  	uint32_t start_word_addr;
  	uint32_t end_word_addr;
  	uint16_t len_to_write;
  	uint32_t len_index;
  	type_bool_t need_erase_page;
  
  	assert_param(IS_FLASH_ADDRESS(addr));
  	assert_param(IS_FLASH_ADDRESS(addr + len - 1));
  
  	len_to_write = len;
  
  	__disable_irq();
  	while (len_to_write > 0) {
  		need_erase_page = FALSE;
  
  		for (index = 0; index < FLASH_PAGE_SIZE; index++)
  			write_buf[index] = 0xFF;
  
  		start_write_addr = addr + (len - len_to_write);
  		end_write_addr   = addr + len - 1;
  		end_write_addr   = FLASH_PAGE_ADDR(start_write_addr) == FLASH_PAGE_ADDR(end_write_addr)
  		                   ? end_write_addr : FLASH_PAGEEND_ADDR(start_write_addr);
  		need_erase_page  = page_have_writen(FLASH_WORD_ADDR(start_write_addr),
  		                                    FLASH_WORDEND_ADDR(end_write_addr));
  
  		if (need_erase_page) {
  			if (ERROR == ald_flash_read((uint32_t *)write_buf, FLASH_PAGE_ADDR(start_write_addr),
                                   		  FLASH_PAGE_SIZE >> 2)) {
  				__enable_irq();
  				return ERROR;
  			}
  
  			if (ERROR == flash_page_erase(FLASH_PAGE_ADDR(start_write_addr))) {
  				__enable_irq();
  				return ERROR;
  			}
  
  			para   = end_write_addr & (FLASH_PAGE_SIZE - 1);
  			index  = start_write_addr & (FLASH_PAGE_SIZE - 1);
  			index2 = len - len_to_write;
  
  			while (index <= para)
  				write_buf[index++] = buf[index2++];
  
  			index2     = 0;
  			index      = FLASH_PAGE_ADDR(start_write_addr);
  			len_index  = FLASH_PAGE_SIZE;
  		}
  		else {
  			para   = end_write_addr & (FLASH_PAGE_SIZE - 1);
  			index  = start_write_addr & (FLASH_PAGE_SIZE - 1);
  			index2 = len - len_to_write;
  
  			while (index <= para)
  				write_buf[index++] = buf[index2++];
  
  			start_word_addr = FLASH_WORD_ADDR(start_write_addr);
  			end_word_addr   = FLASH_WORDEND_ADDR(end_write_addr);
  			index2          = (FLASH_WORD_ADDR(start_word_addr) - FLASH_PAGE_ADDR(start_word_addr));
  			index           = start_word_addr;
  			len_index       = end_word_addr - start_word_addr + 1;
  		}
  
  		if (ERROR == flash_word_program(index, (uint32_t *)(write_buf + index2), (len_index >> 3), FLASH_FIFO)) {
  			__enable_irq();
  			return ERROR;
  		}
  
  		len_to_write = len_to_write - (end_write_addr - start_write_addr + 1);
  	}
  
  	__enable_irq();
  	return OK;
  }
  
  /**
    * @brief  erase The flash between the given address section.
    * @param  addr: The start address to erase.
    * @param  len: The length to erase.
    * @retval Status, see @ref ald_status_t.
    */
  ald_status_t ald_flash_erase(uint32_t addr, uint16_t len)
  {
  	uint32_t index;
  	int32_t para;
  	int32_t start_erase_addr;
  	int32_t end_erase_addr;
  	uint16_t len_not_erase;
  	uint32_t len_index;
  	type_bool_t page_need_save;
  
  	assert_param(IS_FLASH_ADDRESS(addr));
  	assert_param(IS_FLASH_ADDRESS(addr + len - 1));
  
  	len_not_erase = len;
  
  	__disable_irq();
  	while (len_not_erase > 0) {
  		page_need_save = FALSE;
  
  		start_erase_addr = addr + len - len_not_erase;
  		end_erase_addr   = addr + len - 1;
  		end_erase_addr   = (FLASH_PAGE_ADDR(start_erase_addr) == FLASH_PAGE_ADDR(end_erase_addr))
  		                    ? end_erase_addr : FLASH_PAGEEND_ADDR(start_erase_addr);
  
  		if (start_erase_addr != FLASH_PAGE_ADDR(start_erase_addr)) {
  			if (page_have_writen(FLASH_PAGE_ADDR(start_erase_addr), (start_erase_addr - 1)))
  				page_need_save = TRUE;
  		}
  		if (end_erase_addr != FLASH_PAGEEND_ADDR(end_erase_addr)) {
  			if (page_have_writen((end_erase_addr + 1), FLASH_PAGEEND_ADDR(end_erase_addr)))
  				page_need_save = TRUE;
  		}
  
  		if (page_need_save) {
  			if (ERROR == ald_flash_read((uint32_t *)write_buf, FLASH_PAGE_ADDR(start_erase_addr),
  			                                                     FLASH_PAGE_SIZE >> 2)) {
  				__enable_irq();
  				return ERROR;
  			}
  		}
  
  		if (ERROR == flash_page_erase(FLASH_PAGE_ADDR(start_erase_addr))) {
  			__enable_irq();
  			return ERROR;
  		}
  
  		if (page_need_save) {
  			para  = end_erase_addr & (FLASH_PAGE_SIZE - 1);
  			index = start_erase_addr & (FLASH_PAGE_SIZE - 1);
  
  			while (index <= para)
  				write_buf[index++] = 0xFF;
  
  			index     = FLASH_PAGE_ADDR(start_erase_addr);
  			len_index = FLASH_PAGE_SIZE;
  			if (ERROR == flash_word_program(index, (uint32_t *)write_buf, (len_index >> 3), FLASH_FIFO)) {
  				__enable_irq();
  				return ERROR;
  			}
  		}
  		len_not_erase = len_not_erase - (end_erase_addr - start_erase_addr + 1);
  	}
  
  	__enable_irq();
  	return OK;
  }
  /**
    * @}
    */
  
  
  #endif
  
  /**
    * @}
    */
  
  /**
    * @}
    */
  