/**
******************************************************************************
* @file Cloud/AWS/Src/flash.c
* @author MCD Application Team
* @version V1.0.1
* @date 12-April-2017
* @brief Management of the L4 internal flash memory.
******************************************************************************
* @attention
*
*
© Copyright (c) 2017 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution 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 STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS 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.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "flash.h"
#include
#include
#include "FreeRTOS.h"
/* Private typedef -----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
#define ROUND_DOWN(a,b) (((a) / (b)) * (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
/* Private variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Functions Definition ------------------------------------------------------*/
/**
* @brief Erase FLASH memory page(s) at address.
* @note The range to erase shall not cross the bank boundary.
* @param In: address Start address to erase from.
* @param In: len_bytes Length to be erased.
* @retval 0: Success.
-1: Failure.
*/
int FLASH_unlock_erase(uint32_t address, uint32_t len_bytes)
{
int rc = -1;
uint32_t PageError = 0;
FLASH_EraseInitTypeDef EraseInit;
/* L4 ROM memory map, with 2 banks split into 2kBytes pages.
* WARN: ABW. If the passed address and size are not page-aligned,
* the start of the first page and the end of the last page are erased anyway.
* After erase, the flash is left in unlocked state.
*/
EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInit.Banks = FLASH_get_bank(address);
if (EraseInit.Banks != FLASH_get_bank(address + len_bytes))
{
printf("Error: Cannot erase across FLASH banks.\n");
}
else
{
EraseInit.Page = FLASH_get_pageInBank(address);
EraseInit.NbPages = FLASH_get_pageInBank(address + len_bytes - 1) - EraseInit.Page + 1;
HAL_FLASH_Unlock();
if (HAL_FLASHEx_Erase(&EraseInit, &PageError) == HAL_OK)
{
rc = 0;
}
else
{
printf("Error erasing at 0x%08lx\n", address);
}
}
return rc;
}
/**
* @brief Write to FLASH memory.
* @param In: address Destination address.
* @param In: pData Data to be programmed: Must be 8 byte aligned.
* @param In: len_bytes Number of bytes to be programmed.
* @retval 0: Success.
-1: Failure.
*/
int FLASH_write_at(uint32_t address, uint64_t *pData, uint32_t len_bytes)
{
int i;
int ret = -1;
#ifndef CODE_UNDER_FIREWALL
/* irq already mask under firewall */
__disable_irq();
#endif
for (i = 0; i < len_bytes; i += 8)
{
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,
address + i,
*(pData + (i/8) )) != HAL_OK)
{
break;
}
}
/* Memory check */
for (i = 0; i < len_bytes; i += 4)
{
uint32_t *dst = (uint32_t *)(address + i);
uint32_t *src = ((uint32_t *) pData) + (i/4);
if ( *dst != *src )
{
#ifndef CODE_UNDER_FIREWALL
printf("Write failed @0x%08lx, read value=0x%08lx, expected=0x%08lx\n", (uint32_t) dst, *dst, *src);
#endif
break;
}
ret = 0;
}
#ifndef CODE_UNDER_FIREWALL
/* irq should never be enable under firewall */
__enable_irq();
#endif
return ret;
}
/**
* @brief Get the bank of a given address.
* @param In: addr Address in the FLASH Memory.
* @retval Bank identifier.
* FLASH_BANK_1
* FLASH_BANK_2
*/
uint32_t FLASH_get_bank(uint32_t addr)
{
/* The flash bank size is calculated via bits from a memory mapped register. */
uint32_t ulFlashBank2Start = FLASH_BASE + FLASH_BANK_SIZE;
uint32_t bank = 0;
if (READ_BIT(SYSCFG->MEMRMP, SYSCFG_MEMRMP_FB_MODE) == 0)
{
/* No Bank swap */
bank = ( addr < ulFlashBank2Start ) ? FLASH_BANK_1 : FLASH_BANK_2;
}
else
{
/* Bank swap */
bank = ( addr < ulFlashBank2Start ) ? FLASH_BANK_2 : FLASH_BANK_1;
}
return bank;
}
/**
* @brief Get the page of a given address within its FLASH bank.
* @param In: addr Address in the FLASH memory.
* @retval >=0 Success: Page number.
* <0 Failure: The address in not mapped in the internal FLASH memory.
*/
int FLASH_get_pageInBank(uint32_t addr)
{
/* The flash bank size is calculated via bits from a memory mapped register. */
uint32_t ulFlashBank2Start = FLASH_BASE + FLASH_BANK_SIZE;
int page = -1;
if ( ( ulFlashBank2Start > addr ) && ( addr >= FLASH_BASE ) )
{
/* The address is in internal FLASH range. */
if ( addr < ulFlashBank2Start )
{
/* Addr in the first bank */
page = (addr - FLASH_BASE) / FLASH_PAGE_SIZE;
}
else
{
/* Addr in the second bank */
page = (addr - ulFlashBank2Start ) / FLASH_PAGE_SIZE;
}
}
return page;
}
/**
* @brief Update a chunk of the FLASH memory.
* @note The FLASH chunk must no cross a FLASH bank boundary.
* @note The source and destination buffers have no specific alignment constraints.
* @param In: dst_addr Destination address in the FLASH memory.
* @param In: data Source address.
* @param In: size Number of bytes to update.
* @retval 0: Success.
* <0: Failure.
*/
int FLASH_update(uint32_t dst_addr, const void *data, uint32_t size)
{
int ret = 0;
int remaining = size;
uint8_t * src_addr = (uint8_t *) data;
uint64_t * page_cache = pvPortMalloc(FLASH_PAGE_SIZE);
if (page_cache != NULL)
{
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
do {
uint32_t fl_addr = ROUND_DOWN(dst_addr, FLASH_PAGE_SIZE);
int fl_offset = dst_addr - fl_addr;
int len = MIN(FLASH_PAGE_SIZE - fl_offset, size);
/* Load from the flash into the cache */
memcpy(page_cache, (void *) fl_addr, FLASH_PAGE_SIZE);
/* Update the cache from the source */
memcpy((uint8_t *)page_cache + fl_offset, src_addr, len);
/* Erase the page, and write the cache */
ret = FLASH_unlock_erase(fl_addr, FLASH_PAGE_SIZE);
if (ret != 0)
{
printf("Error erasing at 0x%08lx\n", fl_addr);
}
else
{
ret = FLASH_write_at(fl_addr, page_cache, FLASH_PAGE_SIZE);
if(ret != 0)
{
printf("Error writing %lu bytes at 0x%08lx\n", FLASH_PAGE_SIZE, fl_addr);
}
else
{
dst_addr += len;
src_addr += len;
remaining -= len;
}
}
} while ((ret == 0) && (remaining > 0));
vPortFree(page_cache);
}
if (ret == 0)
{
return size;
}
else
{
return -1;
}
}
/**
* @brief Set the FLASH bank from which the bootloader which start after the next reset.
* @note See also the FLASH_BualBoot L4 example project.
* @param bank: Boot target.
* FLASH_BANK_1
* FLASH_BANK_2
* FLASH_BANK_BOTH Switch to the other bank than the one which is currently used.
* @retval 0: Success.
* <0: Failure.
*/
int FLASH_set_boot_bank(uint32_t bank)
{
int rc = 0;
FLASH_OBProgramInitTypeDef OBInit;
/* Set BFB2 bit to enable boot from Flash Bank2 */
/* Allow Access to the Flash control registers and user Flash. */
HAL_FLASH_Unlock();
/* Clear OPTVERR bit set on virgin samples. */
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
/* Allow Access to the option bytes sector. */
HAL_FLASH_OB_Unlock();
/* Get the Dual boot configuration status. */
HAL_FLASHEx_OBGetConfig(&OBInit);
/* Enable/Disable dual boot feature */
OBInit.OptionType = OPTIONBYTE_USER;
OBInit.USERType = OB_USER_BFB2;
switch (bank)
{
case FLASH_BANK_1:
OBInit.USERConfig = OB_BFB2_DISABLE;
break;
case FLASH_BANK_2:
OBInit.USERConfig = OB_BFB2_ENABLE;
break;
case FLASH_BANK_BOTH:
OBInit.USERConfig = ( (OBInit.USERConfig & OB_BFB2_ENABLE) == OB_BFB2_ENABLE ) ? OB_BFB2_DISABLE : OB_BFB2_ENABLE;
break;
default:
rc = -1;
}
if(HAL_FLASHEx_OBProgram (&OBInit) != HAL_OK)
{ /* Failed setting the option bytes configuration.
* Call 'HAL_FLASH_GetError()' for details. */
rc = -1;
printf("Error: Could not init the option bytes programming.\n");
}
#if 0 /* Excluded so we don't force a reset here by loading the option bytes. */
else
{ /* Start the Option Bytes programming process */
if (HAL_FLASH_OB_Launch() != HAL_OK)
{ /* Failed reloading the option bytes configuration.
* Call 'HAL_FLASH_GetError()' for details. */
rc = -1;
printf("Error: Could not program the 2nd bank boot option byte.\n");
}
}
#endif
return rc;
}
int FLASH_get_boot_bank( void )
{
FLASH_OBProgramInitTypeDef OBInit;
/* Set BFB2 bit to enable boot from Flash Bank2 */
/* Allow Access to the Flash control registers and user Flash. */
HAL_FLASH_Unlock();
/* Clear OPTVERR bit set on virgin samples. */
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
/* Allow Access to the option bytes sector. */
HAL_FLASH_OB_Unlock();
/* Get the Dual boot configuration status. */
HAL_FLASHEx_OBGetConfig(&OBInit);
return ( (OBInit.USERConfig & OB_BFB2_ENABLE) == OB_BFB2_ENABLE ) ? FLASH_BANK_2 : FLASH_BANK_1;
}
/* Determine which flash bank we're in and return the base address of the alternate bank. */
uint32_t FLASH_get_alternate_bank_addr( void )
{
/* The flash bank size is calculated via bits from a memory mapped register. */
uint32_t ulFlashBankSize = FLASH_BANK_SIZE;
uint32_t ulFlashBank2Start = FLASH_BASE + ulFlashBankSize;
uint32_t ulFlashBank2End = FLASH_BASE + ( 2 * ulFlashBankSize );
uint32_t cur_addr;
uint32_t alt_addr = 0;
cur_addr = ( uint32_t ) &FLASH_get_alternate_bank_addr;
if ( cur_addr >= FLASH_BASE )
{
/* Is the address in bank 1 range? */
if ( cur_addr < ulFlashBank2Start )
{
/* We're running in bank 1 so return bank 2. */
alt_addr = ulFlashBank2Start;
}
else
{
/* It wasn't in bank 1 so verify it is within bank 2 range. */
if ( cur_addr <= ulFlashBank2End )
/* We're running in bank 2 so return bank 1. */
alt_addr = FLASH_BASE;
}
}
return alt_addr;
}
/* Return the base address of the bank we're executing in. */
uint32_t FLASH_get_current_bank_addr( void )
{
/* The flash bank size is calculated via bits from a memory mapped register. */
uint32_t ulFlashBankSize = FLASH_BANK_SIZE;
uint32_t ulFlashBank2Start = FLASH_BASE + ulFlashBankSize;
uint32_t ulFlashBank2End = FLASH_BASE + ( 2 * ulFlashBankSize );
uint32_t cur_addr;
uint32_t ret_addr = 0;
cur_addr = ( uint32_t ) &FLASH_get_current_bank_addr;
if ( cur_addr >= FLASH_BASE )
{
/* Is the address in bank 1 range? */
if ( cur_addr < ulFlashBank2Start )
{
/* We're running in bank 1 so return bank 1. */
ret_addr = FLASH_BASE;
}
else
{
/* It wasn't in bank 1 so verify it is within bank 2 range. */
if ( cur_addr <= ulFlashBank2End )
/* We're running in bank 2 so return bank 2. */
ret_addr = ulFlashBank2Start;
}
}
return ret_addr;
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/