blob: ceab396805699abf68f8509b477aecbfa25daf04 [file] [log] [blame]
/**
* \file
*
* \brief FlashCALW driver for SAM4L.
*
* Copyright (c) 2012-2013 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions 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. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
*
* \asf_license_stop
*
*/
#include "flashcalw.h"
#include "sysclk.h"
/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
extern "C" {
#endif
/**INDENT-ON**/
/// @endcond
/**
* \defgroup group_sam_drivers_flashcalw FLASHCALW - FLASH Controller Double-Word
*
* See \ref sam_flashcalw_quickstart.
*
* FLASHCALW interfaces a flash block with the 32-bit internal HSB bus.
*
* \{
*/
/*! \name Flash Properties
*/
//! @{
/*! \brief Gets the size of the whole flash array.
*
* \return The size of the whole flash array in Bytes.
*/
uint32_t flashcalw_get_flash_size(void)
{
const uint16_t flash_size[(FLASHCALW_FPR_FSZ_Msk >>
FLASHCALW_FPR_FSZ_Pos) + 1] = {
4,
8,
16,
32,
48,
64,
96,
128,
192,
256,
384,
512,
768,
1024,
2048,
};
return ((uint32_t)flash_size[(HFLASHC->FLASHCALW_FPR &
FLASHCALW_FPR_FSZ_Msk) >> FLASHCALW_FPR_FSZ_Pos] << 10);
}
/*! \brief Gets the total number of pages in the flash array.
*
* \return The total number of pages in the flash array.
*/
uint32_t flashcalw_get_page_count(void)
{
return flashcalw_get_flash_size() / FLASH_PAGE_SIZE;
}
/*! \brief Gets the number of pages in each flash region.
*
* \return The number of pages in each flash region.
*/
uint32_t flashcalw_get_page_count_per_region(void)
{
return flashcalw_get_page_count() / FLASH_NB_OF_REGIONS;
}
/*! \brief Gets the region number of a page.
*
* \param page_number The page number:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: the current page number.
*
* \return The region number of the specified page.
*/
uint32_t flashcalw_get_page_region(int32_t page_number)
{
return ((page_number >= 0) ? page_number
: (int32_t)flashcalw_get_page_number())
/ flashcalw_get_page_count_per_region();
}
/*! \brief Gets the number of the first page of a region.
*
* \param region The region number: \c 0 to <tt>(FLASHCALW_REGIONS - 1)</tt>.
*
* \return The number of the first page of the specified region.
*/
uint32_t flashcalw_get_region_first_page_number(uint32_t region)
{
return region * flashcalw_get_page_count_per_region();
}
//! @}
/*! \name FLASHC Control
*/
//! @{
/*! \brief Gets the number of wait states of flash read accesses.
*
* \return The number of wait states of flash read accesses.
*/
uint32_t flashcalw_get_wait_state(void)
{
return (HFLASHC->FLASHCALW_FCR & FLASHCALW_FCR_FWS ? 1 : 0);
}
/*! \brief Sets the number of wait states of flash read accesses.
*
* \param wait_state The number of wait states of flash read accesses: \c 0 to
* \c 1.
*/
void flashcalw_set_wait_state(uint32_t wait_state)
{
HFLASHC->FLASHCALW_FCR = (HFLASHC->FLASHCALW_FCR & ~FLASHCALW_FCR_FWS)
| (wait_state ? FLASHCALW_FCR_FWS_1 :
FLASHCALW_FCR_FWS_0);
}
#define FLASH_FWS_0_MAX_FREQ CHIP_FREQ_FWS_0
#define FLASH_FWS_1_MAX_FREQ CHIP_FREQ_FWS_1
#define FLASH_HSEN_FWS_0_MAX_FREQ CHIP_FREQ_FLASH_HSEN_FWS_0
#define FLASH_HSEN_FWS_1_MAX_FREQ CHIP_FREQ_FLASH_HSEN_FWS_1
/*! \brief Depending on the CPU frequency, on the Power Scaling mode and on the
* Fast Wakeup mode, set the wait states of flash read accesses and enable or
* disable the High speed read mode.
*
* \param cpu_f_hz The CPU frequency
* \param ps_value Power Scaling mode value (0, 1)
* \param is_fwu_enabled (boolean), Is fast wakeup mode enabled or not
*/
void flashcalw_set_flash_waitstate_and_readmode(uint32_t cpu_f_hz,
uint32_t ps_value, bool is_fwu_enabled)
{
#ifdef CONFIG_FLASH_READ_MODE_HIGH_SPEED_ENABLE
UNUSED(ps_value);
UNUSED(is_fwu_enabled);
if (cpu_f_hz > FLASH_FREQ_PS2_FWS_0_MAX_FREQ) { /* > 24MHz */
/* Set a wait-state. */
flashcalw_set_wait_state(1);
} else {
/* No wait-state. */
flashcalw_set_wait_state(0);
}
/* Enable the high-speed read mode. */
flashcalw_issue_command(FLASHCALW_FCMD_CMD_HSEN, -1);
#else
if (ps_value == 0) {
if (cpu_f_hz > FLASH_FREQ_PS0_FWS_0_MAX_FREQ) {
// > 18MHz
if (cpu_f_hz <= FLASH_FREQ_PS0_FWS_1_MAX_FREQ) {
// <= 36MHz
/* Set a wait-state, disable the high-speed read
* mode. */
flashcalw_set_wait_state(1);
flashcalw_issue_command(
FLASHCALW_FCMD_CMD_HSDIS, -1);
} else {
// > 36 MHz
/* Set a wait-state, enable the high-speed read
mode. */
flashcalw_set_wait_state(1);
flashcalw_issue_command(FLASHCALW_FCMD_CMD_HSEN,
-1);
}
} else { // <= 18MHz
if((is_fwu_enabled == true) &&
(cpu_f_hz <= FLASH_FREQ_PS1_FWS_1_FWU_MAX_FREQ))
{
// <= 12MHz
/* Set a wait-state, disable the high-speed read
mode. */
flashcalw_set_wait_state(1);
flashcalw_issue_command(
FLASHCALW_FCMD_CMD_HSDIS, -1);
} else {
/* No wait-state, disable the high-speed read
mode */
flashcalw_set_wait_state(0);
flashcalw_issue_command(
FLASHCALW_FCMD_CMD_HSDIS, -1);
}
}
} else { /* ps_value == 1 */
if (cpu_f_hz > FLASH_FREQ_PS0_FWS_0_MAX_FREQ) { /* > 8MHz */
/* Set a wait-state. */
flashcalw_set_wait_state(1);
} else {
/* No wait-state. */
flashcalw_set_wait_state(0);
}
/* Disable the high-speed read mode. */
flashcalw_issue_command(FLASHCALW_FCMD_CMD_HSDIS, -1);
}
#endif
}
/*! \brief Tells whether the Flash Ready interrupt is enabled.
*
* \return Whether the Flash Ready interrupt is enabled.
*/
bool flashcalw_is_ready_int_enabled(void)
{
return ((HFLASHC->FLASHCALW_FCR & FLASHCALW_FCR_FRDY) != 0);
}
/*! \brief Enables or disables the Flash Ready interrupt.
*
* \param enable Whether to enable the Flash Ready interrupt: \c true or
* \c false.
*/
void flashcalw_enable_ready_int(bool enable)
{
HFLASHC->FLASHCALW_FCR
&= ((enable &
FLASHCALW_FCR_FRDY) | (~FLASHCALW_FCR_FRDY));
}
/*! \brief Tells whether the Lock Error interrupt is enabled.
*
* \return Whether the Lock Error interrupt is enabled.
*/
bool flashcalw_is_lock_error_int_enabled(void)
{
return ((HFLASHC->FLASHCALW_FCR & FLASHCALW_FCR_LOCKE) != 0);
}
/*! \brief Enables or disables the Lock Error interrupt.
*
* \param enable Whether to enable the Lock Error interrupt: \c true or
* \c false.
*/
void flashcalw_enable_lock_error_int(bool enable)
{
HFLASHC->FLASHCALW_FCR
&= ((enable &
FLASHCALW_FCR_LOCKE) | (~FLASHCALW_FCR_LOCKE));
}
/*! \brief Tells whether the Programming Error interrupt is enabled.
*
* \return Whether the Programming Error interrupt is enabled.
*/
bool flashcalw_is_prog_error_int_enabled(void)
{
return ((HFLASHC->FLASHCALW_FCR & FLASHCALW_FCR_PROGE) != 0);
}
/*! \brief Enables or disables the Programming Error interrupt.
*
* \param enable Whether to enable the Programming Error interrupt: \c true or
* \c false.
*/
void flashcalw_enable_prog_error_int(bool enable)
{
HFLASHC->FLASHCALW_FCR &= ((enable &
FLASHCALW_FCR_PROGE) | (~FLASHCALW_FCR_PROGE));
}
//! @}
/*! \name FLASHCALW Status
*/
//! @{
/*! \brief Tells whether the FLASHCALW is ready to run a new command.
*
* \return Whether the FLASHCALW is ready to run a new command.
*/
bool flashcalw_is_ready(void)
{
return ((HFLASHC->FLASHCALW_FSR & FLASHCALW_FSR_FRDY) != 0);
}
/*! \brief Waits actively until the FLASHCALW is ready to run a new command.
*
* This is the default function assigned to \ref flashcalw_wait_until_ready.
*/
void flashcalw_default_wait_until_ready(void)
{
while (!flashcalw_is_ready()) {
}
}
/**
* \brief Pointer to the function used by the driver when it needs to wait until
* the FLASHCALW is ready to run a new command.
*
* The default function is \ref flashcalw_default_wait_until_ready.
* The user may change this pointer to use another implementation.
*/
void(*volatile flashcalw_wait_until_ready) (void)
= flashcalw_default_wait_until_ready;
/**
* \internal
* \brief Gets the error status of the FLASHCALW.
*
* \return The error status of the FLASHCALW built up from
* \c FLASHCALW_FSR_LOCKE and \c FLASHCALW_FSR_PROGE.
*
* \warning This hardware error status is cleared by all functions reading the
* Flash Status Register (FSR). This function is therefore not part of
* the driver's API which instead presents \ref flashcalw_is_lock_error
* and \ref flashcalw_is_programming_error.
*/
static uint32_t flashcalw_get_error_status(void)
{
return HFLASHC->FLASHCALW_FSR &
(FLASHCALW_FSR_LOCKE | FLASHCALW_FSR_PROGE);
}
/**
* \internal
* \brief Sticky error status of the FLASHCALW.
*
* This variable is updated by functions that issue FLASHCALW commands. It
* contains the cumulated FLASHCALW error status of all the FLASHCALW commands
* issued by a function.
*/
static uint32_t flashcalw_error_status = 0;
/*! \brief Tells whether a Lock Error has occurred during the last function
* called that issued one or more FLASHCALW commands.
*
* \return Whether a Lock Error has occurred during the last function called
* that issued one or more FLASHCALW commands.
*/
bool flashcalw_is_lock_error(void)
{
return ((flashcalw_error_status & FLASHCALW_FSR_LOCKE) != 0);
}
/*! \brief Tells whether a Programming Error has occurred during the last
* function called that issued one or more FLASHCALW commands.
*
* \return Whether a Programming Error has occurred during the last function
* called that issued one or more FLASHCALW commands.
*/
bool flashcalw_is_programming_error(void)
{
return ((flashcalw_error_status & FLASHCALW_FSR_PROGE) != 0);
}
//! @}
/*! \name FLASHCALW Command Control
*/
//! @{
/*! \brief Gets the last issued FLASHCALW command.
*
* \return The last issued FLASHCALW command.
*/
uint32_t flashcalw_get_command(void)
{
return (HFLASHC->FLASHCALW_FCMD & FLASHCALW_FCMD_CMD_Msk);
}
/*! \brief Gets the current FLASHCALW page number.
*
* \return The current FLASHCALW page number.
*/
uint32_t flashcalw_get_page_number(void)
{
return ((HFLASHC->FLASHCALW_FCMD & FLASHCALW_FCMD_PAGEN_Msk)
>> FLASHCALW_FCMD_PAGEN_Pos);
}
/*! \brief Issues a FLASHCALW command.
*
* \param command The command: \c FLASHCALW_FCMD_CMD_x.
* \param page_number The page number to apply the command to:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: use this to apply the command to the current page number
* or if the command does not apply to any page number;
* \arg this argument may have other meanings according to the command. See
* the FLASHCALW chapter of the MCU datasheet.
*
* \warning A Lock Error is issued if the command violates the protection
* mechanism.
*
* \warning A Programming Error is issued if the command is invalid.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_issue_command(uint32_t command, int page_number)
{
uint32_t tempo;
flashcalw_wait_until_ready();
tempo = HFLASHC->FLASHCALW_FCMD;
/* Clear the command bitfield. */
tempo &= ~FLASHCALW_FCMD_CMD_Msk;
if (page_number >= 0) {
tempo = (FLASHCALW_FCMD_KEY_KEY
| FLASHCALW_FCMD_PAGEN(page_number) | command);
} else {
tempo |= (FLASHCALW_FCMD_KEY_KEY | command);
}
HFLASHC->FLASHCALW_FCMD = tempo;
flashcalw_error_status = flashcalw_get_error_status();
flashcalw_wait_until_ready();
}
//! @}
/*! \name FLASHCALW Global Commands
*/
//! @{
/*! \brief Issues a No Operation command to the FLASHCALW.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_no_operation(void)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_NOP, -1);
}
/*! \brief Issues an Erase All command to the FLASHCALW.
*
* This command erases all bits in the flash array, the general-purpose fuse
* bits and the Security bit. The User page is not erased.
*
* This command also ensures that all volatile memories, such as register file
* and RAMs, are erased before the Security bit is erased, i.e. deactivated.
*
* \warning A Lock Error is issued if at least one region is locked or the
* bootloader protection is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*/
void flashcalw_erase_all(void)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_EA, -1);
}
//! @}
/*! \name FLASHCALW Protection Mechanisms
*/
//! @{
/*! \brief Tells whether the Security bit is active.
*
* \return Whether the Security bit is active.
*/
bool flashcalw_is_security_bit_active(void)
{
return ((HFLASHC->FLASHCALW_FSR & FLASHCALW_FSR_SECURITY) != 0);
}
/*! \brief Activates the Security bit.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_set_security_bit(void)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_SSB, -1);
}
/*! \brief Tells whether the region of a page is locked.
*
* \param page_number The page number:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: the current page number.
*
* \return Whether the region of the specified page is locked.
*/
bool flashcalw_is_page_region_locked(uint32_t page_number)
{
return flashcalw_is_region_locked(flashcalw_get_page_region(page_number));
}
/*! \brief Tells whether a region is locked.
*
* \param region The region number: \c 0 to <tt>(FLASHCALW_REGIONS - 1)</tt>.
*
* \return Whether the specified region is locked.
*/
bool flashcalw_is_region_locked(uint32_t region)
{
return ((HFLASHC->FLASHCALW_FSR & FLASHCALW_FSR_LOCK0
<< (region & (FLASHCALW_REGIONS - 1))) != 0);
}
/*! \brief Locks or unlocks the region of a page.
*
* \param page_number The page number:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: the current page number.
* \param lock Whether to lock the region of the specified page: \c true or
* \c false.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_lock_page_region(int page_number, bool lock)
{
flashcalw_issue_command(
(lock) ? FLASHCALW_FCMD_CMD_LP : FLASHCALW_FCMD_CMD_UP,
page_number);
}
/*! \brief Locks or unlocks a region.
*
* \param region The region number: \c 0 to <tt>(FLASHCALW_REGIONS - 1)</tt>.
* \param lock Whether to lock the specified region: \c true or \c false.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_lock_region(uint32_t region, bool lock)
{
flashcalw_lock_page_region(flashcalw_get_region_first_page_number(
region), lock);
}
/*! \brief Locks or unlocks all regions.
*
* \param lock Whether to lock the regions: \c true or \c false.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_lock_all_regions(bool lock)
{
uint32_t error_status = 0;
uint32_t region = FLASHCALW_REGIONS;
while (region) {
flashcalw_lock_region(--region, lock);
error_status |= flashcalw_error_status;
}
flashcalw_error_status = error_status;
}
//! @}
/*! \name Access to General-Purpose Fuses
*/
//! @{
/*! \brief Reads a general-purpose fuse bit.
*
* \param gp_fuse_bit The general-purpose fuse bit: \c 0 to \c 63.
*
* \return The value of the specified general-purpose fuse bit.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
bool flashcalw_read_gp_fuse_bit(uint32_t gp_fuse_bit)
{
return ((flashcalw_read_all_gp_fuses() & 1ULL << (gp_fuse_bit & 0x3F))
!= 0);
}
/*! \brief Reads a general-purpose fuse bit-field.
*
* \param pos The bit-position of the general-purpose fuse bit-field: \c 0 to
* \c 63.
* \param width The bit-width of the general-purpose fuse bit-field: \c 0 to
* \c 64.
*
* \return The value of the specified general-purpose fuse bit-field.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
uint64_t flashcalw_read_gp_fuse_bitfield(uint32_t pos, uint32_t width)
{
return flashcalw_read_all_gp_fuses() >> (pos & 0x3F)
& ((1ULL << Min(width, 64)) - 1);
}
/*! \brief Reads a general-purpose fuse byte.
*
* \param gp_fuse_byte The general-purpose fuse byte: \c 0 to \c 7.
*
* \return The value of the specified general-purpose fuse byte.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
uint8_t flashcalw_read_gp_fuse_byte(uint32_t gp_fuse_byte)
{
return flashcalw_read_all_gp_fuses() >> ((gp_fuse_byte & 0x07) << 3);
}
/*! \brief Reads all general-purpose fuses.
*
* \return The value of all general-purpose fuses as a word.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
uint64_t flashcalw_read_all_gp_fuses(void)
{
return HFLASHC->FLASHCALW_FGPFRLO |
(uint64_t)HFLASHC->FLASHCALW_FGPFRHI << 32;
}
/*! \brief Erases a general-purpose fuse bit.
*
* \param gp_fuse_bit The general-purpose fuse bit: \c 0 to \c 63.
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \warning A Lock Error is issued if the Security bit is active and the command
* is applied to BOOTPROT.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
bool flashcalw_erase_gp_fuse_bit(uint32_t gp_fuse_bit, bool check)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_EGPB, gp_fuse_bit & 0x3F);
return (check) ? flashcalw_read_gp_fuse_bit(gp_fuse_bit) : true;
}
/*! \brief Erases a general-purpose fuse bit-field.
*
* \param pos The bit-position of the general-purpose fuse bit-field: \c 0 to
* \c 63.
* \param width The bit-width of the general-purpose fuse bit-field: \c 0 to
* \c 64.
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \warning A Lock Error is issued if the Security bit is active and the command
* is applied to BOOTPROT.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
bool flashcalw_erase_gp_fuse_bitfield(uint32_t pos, uint32_t width, bool check)
{
uint32_t error_status = 0;
uint32_t gp_fuse_bit;
pos &= 0x3F;
width = Min(width, 64);
for (gp_fuse_bit = pos; gp_fuse_bit < pos + width; gp_fuse_bit++) {
flashcalw_erase_gp_fuse_bit(gp_fuse_bit, false);
error_status |= flashcalw_error_status;
}
flashcalw_error_status = error_status;
return (check) ? (flashcalw_read_gp_fuse_bitfield(pos, width)
== (1ULL << width) - 1) : true;
}
/*! \brief Erases a general-purpose fuse byte.
*
* \param gp_fuse_byte The general-purpose fuse byte: \c 0 to \c 7.
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \warning A Lock Error is issued if the Security bit is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
bool flashcalw_erase_gp_fuse_byte(uint32_t gp_fuse_byte, bool check)
{
uint32_t error_status;
uint32_t current_gp_fuse_byte;
uint64_t value = flashcalw_read_all_gp_fuses();
flashcalw_erase_all_gp_fuses(false);
error_status = flashcalw_error_status;
for (current_gp_fuse_byte = 0; current_gp_fuse_byte < 8;
current_gp_fuse_byte++, value >>= 8) {
if (current_gp_fuse_byte != gp_fuse_byte) {
flashcalw_write_gp_fuse_byte(current_gp_fuse_byte,
value);
error_status |= flashcalw_error_status;
}
}
flashcalw_error_status = error_status;
return (check) ? (flashcalw_read_gp_fuse_byte(gp_fuse_byte) == 0xFF)
: true;
}
/*! \brief Erases all general-purpose fuses.
*
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \warning A Lock Error is issued if the Security bit is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
bool flashcalw_erase_all_gp_fuses(bool check)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_EAGPF, -1);
return (check) ? (flashcalw_read_all_gp_fuses() ==
0xFFFFFFFFFFFFFFFFULL) : true;
}
/*! \brief Writes a general-purpose fuse bit.
*
* \param gp_fuse_bit The general-purpose fuse bit: \c 0 to \c 63.
* \param value The value of the specified general-purpose fuse bit.
*
* \warning A Lock Error is issued if the Security bit is active and the command
* is applied to BOOTPROT.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note A write operation can only clear bits; in other words, an erase
* operation must first be done if some bits need to be set to 1.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_write_gp_fuse_bit(uint32_t gp_fuse_bit, bool value)
{
if (!value) {
flashcalw_issue_command(FLASHCALW_FCMD_CMD_WGPB, gp_fuse_bit
& 0x3F);
}
}
/*! \brief Writes a general-purpose fuse bit-field.
*
* \param pos The bit-position of the general-purpose fuse bit-field: \c 0 to
* \c 63.
* \param width The bit-width of the general-purpose fuse bit-field: \c 0 to
* \c 64.
* \param value The value of the specified general-purpose fuse bit-field.
*
* \warning A Lock Error is issued if the Security bit is active and the command
* is applied to BOOTPROT.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note A write operation can only clear bits; in other words, an erase
* operation must first be done if some bits need to be set to 1.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_write_gp_fuse_bitfield(uint32_t pos, uint32_t width,
uint64_t value)
{
uint32_t error_status = 0;
uint32_t gp_fuse_bit;
pos &= 0x3F;
width = Min(width, 64);
for (gp_fuse_bit = pos; gp_fuse_bit < pos + width;
gp_fuse_bit++, value >>= 1) {
flashcalw_write_gp_fuse_bit(gp_fuse_bit, value & 0x01);
error_status |= flashcalw_error_status;
}
flashcalw_error_status = error_status;
}
/*! \brief Writes a general-purpose fuse byte.
*
* \param gp_fuse_byte The general-purpose fuse byte: \c 0 to \c 7.
* \param value The value of the specified general-purpose fuse byte.
*
* \warning A Lock Error is issued if the Security bit is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note A write operation can only clear bits; in other words, an erase
* operation must first be done if some bits need to be set to 1.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_write_gp_fuse_byte(uint32_t gp_fuse_byte, uint8_t value)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_PGPFB, (gp_fuse_byte & 0x07)
| value << 3);
}
/*! \brief Writes all general-purpose fuses.
*
* \param value The value of all general-purpose fuses as a word.
*
* \warning A Lock Error is issued if the Security bit is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note A write operation can only clear bits; in other words, an erase
* operation must first be done if some bits need to be set to 1.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_write_all_gp_fuses(uint64_t value)
{
uint32_t error_status = 0;
uint32_t gp_fuse_byte;
for (gp_fuse_byte = 0; gp_fuse_byte < 8; gp_fuse_byte++, value >>= 8) {
flashcalw_write_gp_fuse_byte(gp_fuse_byte, value);
error_status |= flashcalw_error_status;
}
flashcalw_error_status = error_status;
}
/*! \brief Sets a general-purpose fuse bit with the appropriate erase and write
* operations.
*
* \param gp_fuse_bit The general-purpose fuse bit: \c 0 to \c 63.
* \param value The value of the specified general-purpose fuse bit.
*
* \warning A Lock Error is issued if the Security bit is active and the command
* is applied to BOOTPROT.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_set_gp_fuse_bit(uint32_t gp_fuse_bit, bool value)
{
if (value) {
flashcalw_erase_gp_fuse_bit(gp_fuse_bit, false);
} else {
flashcalw_write_gp_fuse_bit(gp_fuse_bit, false);
}
}
/*! \brief Sets a general-purpose fuse bit-field with the appropriate erase and
* write operations.
*
* \param pos The bit-position of the general-purpose fuse bit-field: \c 0 to
* \c 63.
* \param width The bit-width of the general-purpose fuse bit-field: \c 0 to
* \c 64.
* \param value The value of the specified general-purpose fuse bit-field.
*
* \warning A Lock Error is issued if the Security bit is active and the command
* is applied to BOOTPROT.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_set_gp_fuse_bitfield(uint32_t pos, uint32_t width,
uint64_t value)
{
uint32_t error_status = 0;
uint32_t gp_fuse_bit;
pos &= 0x3F;
width = Min(width, 64);
for (gp_fuse_bit = pos; gp_fuse_bit < pos + width;
gp_fuse_bit++, value >>= 1) {
flashcalw_set_gp_fuse_bit(gp_fuse_bit, value & 0x01);
error_status |= flashcalw_error_status;
}
flashcalw_error_status = error_status;
}
/*! \brief Sets a general-purpose fuse byte with the appropriate erase and write
* operations.
*
* \param gp_fuse_byte The general-purpose fuse byte: \c 0 to \c 7.
* \param value The value of the specified general-purpose fuse byte.
*
* \warning A Lock Error is issued if the Security bit is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_set_gp_fuse_byte(uint32_t gp_fuse_byte, uint8_t value)
{
uint32_t error_status;
switch (value) {
case 0xFF:
flashcalw_erase_gp_fuse_byte(gp_fuse_byte, false);
break;
case 0x00:
flashcalw_write_gp_fuse_byte(gp_fuse_byte, 0x00);
break;
default:
flashcalw_erase_gp_fuse_byte(gp_fuse_byte, false);
error_status = flashcalw_error_status;
flashcalw_write_gp_fuse_byte(gp_fuse_byte, value);
flashcalw_error_status |= error_status;
break;
}
}
/*! \brief Sets all general-purpose fuses with the appropriate erase and write
* operations.
*
* \param value The value of all general-purpose fuses as a word.
*
* \warning A Lock Error is issued if the Security bit is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note The actual number of general-purpose fuse bits implemented by hardware
* is given by \c FLASH_GPF_NUM. The other bits among the 64 are
* fixed at 1 by hardware.
*/
void flashcalw_set_all_gp_fuses(uint64_t value)
{
uint32_t error_status;
switch (value) {
case 0xFFFFFFFFFFFFFFFFULL:
flashcalw_erase_all_gp_fuses(false);
break;
case 0x0000000000000000ULL:
flashcalw_write_all_gp_fuses(0x0000000000000000ULL);
break;
default:
flashcalw_erase_all_gp_fuses(false);
error_status = flashcalw_error_status;
flashcalw_write_all_gp_fuses(value);
flashcalw_error_status |= error_status;
break;
}
}
//! @}
/*! \name Access to Flash Pages
*/
//! @{
/*! \brief Clears the page buffer.
*
* This command resets all bits in the page buffer to one. Write accesses to the
* page buffer can only change page buffer bits from one to zero.
*
* \warning The page buffer is not automatically reset after a page write.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
void flashcalw_clear_page_buffer(void)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_CPB, -1);
}
/*! \brief Tells whether the page to which the last Quick Page Read or Quick
* Page Read User Page command was applied was erased.
*
* \return Whether the page to which the last Quick Page Read or Quick Page Read
* User Page command was applied was erased.
*/
bool flashcalw_is_page_erased(void)
{
return ((HFLASHC->FLASHCALW_FSR & FLASHCALW_FSR_QPRR) != 0);
}
/*! \brief Applies the Quick Page Read command to a page.
*
* \param page_number The page number:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: the current page number.
*
* \return Whether the specified page is erased.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
bool flashcalw_quick_page_read(int page_number)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_QPR, page_number);
return flashcalw_is_page_erased();
}
/*! \brief Erases a page.
*
* \param page_number The page number:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: the current page number.
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \warning A Lock Error is issued if the command is applied to a page belonging
* to a locked region or to the bootloader protected area.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*/
bool flashcalw_erase_page(int page_number, bool check)
{
bool page_erased = true;
flashcalw_issue_command(FLASHCALW_FCMD_CMD_EP, page_number);
if (check) {
uint32_t error_status = flashcalw_error_status;
page_erased = flashcalw_quick_page_read(-1);
flashcalw_error_status |= error_status;
}
return page_erased;
}
/*! \brief Erases all pages within the flash array.
*
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \warning A Lock Error is issued if at least one region is locked or the
* bootloader protection is active.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*/
bool flashcalw_erase_all_pages(bool check)
{
bool all_pages_erased = true;
uint32_t error_status = 0;
uint32_t page_number = flashcalw_get_page_count();
while (page_number) {
all_pages_erased &= flashcalw_erase_page(--page_number, check);
error_status |= flashcalw_error_status;
}
flashcalw_error_status = error_status;
return all_pages_erased;
}
/*! \brief Writes a page from the page buffer.
*
* \param page_number The page number:
* \arg \c 0 to <tt>(flashcalw_get_page_count() - 1)</tt>: a page number
* within the flash array;
* \arg <tt>< 0</tt>: the current page number.
*
* \warning A Lock Error is issued if the command is applied to a page belonging
* to a locked region or to the bootloader protected area.
*
* \warning The page buffer is not automatically reset after a page write.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note A write operation can only clear bits; in other words, an erase
* operation must first be done if some bits need to be set to 1.
*/
void flashcalw_write_page(int page_number)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_WP, page_number);
}
/*! \brief Issues a Quick Page Read User Page command to the FLASHCALW.
*
* \return Whether the User page is erased.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
bool flashcalw_quick_user_page_read(void)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_QPRUP, -1);
return flashcalw_is_page_erased();
}
/*! \brief Erases the User page.
*
* \param check Whether to check erase: \c true or \c false.
*
* \return Whether the erase succeeded or always \c true if erase check was not
* requested.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note An erase operation can only set bits.
*/
bool flashcalw_erase_user_page(bool check)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_EUP, -1);
return (check) ? flashcalw_quick_user_page_read() : true;
}
/*! \brief Writes the User page from the page buffer.
*
* \warning The page buffer is not automatically reset after a page write.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*
* \note A write operation can only clear bits; in other words, an erase
* operation must first be done if some bits need to be set to 1.
*/
void flashcalw_write_user_page(void)
{
flashcalw_issue_command(FLASHCALW_FCMD_CMD_WUP, -1);
}
/*! \brief Copies \a nbytes bytes to the flash destination pointed to by \a dst
* from the repeated \a src source byte.
*
* All pointer and size alignments are supported.
*
* \param dst Pointer to flash destination.
* \param src Source byte.
* \param nbytes Number of bytes to set.
* \param erase Whether to erase before writing: \c true or \c false.
*
* \return The value of \a dst.
*
* \warning This function may be called with \a erase set to \c false only if
* the destination consists only of erased words, i.e. this function
* cannot be used to write only one bit of a previously written word.
* E.g., if \c 0x00000001 then \c 0xFFFFFFFE are written to a word, the
* resulting value in flash may be different from \c 0x00000000.
*
* \warning A Lock Error is issued if the command is applied to pages belonging
* to a locked region or to the bootloader protected area.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
volatile void *flashcalw_memset8(volatile void *dst, uint8_t src, size_t nbytes,
bool erase)
{
return flashcalw_memset16(dst, src | (uint16_t)src << 8, nbytes, erase);
}
/*! \brief Copies \a nbytes bytes to the flash destination pointed to by \a dst
* from the repeated \a src big-endian source half-word.
*
* All pointer and size alignments are supported.
*
* \param dst Pointer to flash destination.
* \param src Source half-word.
* \param nbytes Number of bytes to set.
* \param erase Whether to erase before writing: \c true or \c false.
*
* \return The value of \a dst.
*
* \warning This function may be called with \a erase set to \c false only if
* the destination consists only of erased words, i.e. this function
* can not be used to write only one bit of a previously written word.
* E.g., if \c 0x00000001 then \c 0xFFFFFFFE are written to a word, the
* resulting value in flash may be different from \c 0x00000000.
*
* \warning A Lock Error is issued if the command is applied to pages belonging
* to a locked region or to the bootloader protected area.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
volatile void *flashcalw_memset16(volatile void *dst, uint16_t src,
size_t nbytes, bool erase)
{
return flashcalw_memset32(dst, src | (uint32_t)src << 16, nbytes,
erase);
}
/*! \brief Copies \a nbytes bytes to the flash destination pointed to by \a dst
* from the repeated \a src big-endian source word.
*
* All pointer and size alignments are supported.
*
* \param dst Pointer to flash destination.
* \param src Source word.
* \param nbytes Number of bytes to set.
* \param erase Whether to erase before writing: \c true or \c false.
*
* \return The value of \a dst.
*
* \warning This function may be called with \a erase set to \c false only if
* the destination consists only of erased words, i.e. this function
* can not be used to write only one bit of a previously written word.
* E.g., if \c 0x00000001 then \c 0xFFFFFFFE are written to a word, the
* resulting value in flash may be different from \c 0x00000000.
*
* \warning A Lock Error is issued if the command is applied to pages belonging
* to a locked region or to the bootloader protected area.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
volatile void *flashcalw_memset32(volatile void *dst, uint32_t src,
size_t nbytes, bool erase)
{
return flashcalw_memset64(dst, src | (uint64_t)src << 32, nbytes,
erase);
}
/*! \brief Copies \a nbytes bytes to the flash destination pointed to by \a dst
* from the repeated \a src big-endian source double-word.
*
* All pointer and size alignments are supported.
*
* \param dst Pointer to flash destination.
* \param src Source double-word.
* \param nbytes Number of bytes to set.
* \param erase Whether to erase before writing: \c true or \c false.
*
* \return The value of \a dst.
*
* \warning This function may be called with \a erase set to \c false only if
* the destination consists only of erased words, i.e. this function
* can not be used to write only one bit of a previously written word.
* E.g., if \c 0x00000001 then \c 0xFFFFFFFE are written to a word, the
* resulting value in flash may be different from \c 0x00000000.
*
* \warning A Lock Error is issued if the command is applied to pages belonging
* to a locked region or to the bootloader protected area.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
volatile void *flashcalw_memset64(volatile void *dst, uint64_t src,
size_t nbytes, bool erase)
{
/* Use aggregated pointers to have several alignments available for a
* same address. */
UnionCVPtr flash_array_end;
UnionVPtr dest;
Union64 source = {0};
StructCVPtr dest_end;
UnionCVPtr flash_page_source_end;
bool incomplete_flash_page_end;
Union64 flash_dword;
UnionVPtr tmp;
uint32_t error_status = 0;
uint32_t i;
/* Reformat arguments. */
flash_array_end.u8ptr = ((uint8_t *)FLASH_ADDR)
+ flashcalw_get_flash_size();
dest.u8ptr = dst;
for (i = (Get_align((uint32_t)dest.u8ptr, sizeof(uint64_t)) - 1)
& (sizeof(uint64_t) - 1); src;
i = (i - 1) & (sizeof(uint64_t) - 1)) {
source.u8[i] = src;
src >>= 8;
}
dest_end.u8ptr = dest.u8ptr + nbytes;
/* If destination is outside flash, go to next flash page if any. */
if (dest.u8ptr < (uint8_t *)FLASH_ADDR) {
dest.u8ptr = (uint8_t *)FLASH_ADDR;
} else if (flash_array_end.u8ptr <= dest.u8ptr &&
dest.u8ptr < (uint8_t *)FLASH_USER_PAGE_ADDR) {
dest.u8ptr = (uint8_t *)FLASH_USER_PAGE_ADDR;
}
/* If end of destination is outside flash, move it to the end of the
* previous flash page if any. */
if (dest_end.u8ptr >
(((uint8_t *)FLASH_USER_PAGE_ADDR) + FLASH_PAGE_SIZE)) {
dest_end.u8ptr = (uint8_t *)FLASH_USER_PAGE_ADDR
+ FLASH_PAGE_SIZE;
} else if ((uint8_t *)FLASH_USER_PAGE_ADDR >= dest_end.u8ptr &&
dest_end.u8ptr > flash_array_end.u8ptr) {
dest_end.u8ptr = flash_array_end.u8ptr;
}
/* Align each end of destination pointer with its natural boundary. */
dest_end.u16ptr = (uint16_t *)Align_down((uint32_t)dest_end.u8ptr,
sizeof(uint16_t));
dest_end.u32ptr = (uint32_t *)Align_down((uint32_t)dest_end.u16ptr,
sizeof(uint32_t));
dest_end.u64ptr = (uint64_t *)Align_down((uint32_t)dest_end.u32ptr,
sizeof(uint64_t));
/* While end of destination is not reached... */
while (dest.u8ptr < dest_end.u8ptr) {
/* Clear the page buffer in order to prepare data for a flash
* page write. */
flashcalw_clear_page_buffer();
error_status |= flashcalw_error_status;
/* Determine where the source data will end in the current flash
* page. */
flash_page_source_end.u64ptr
= (uint64_t *)Min((uint32_t)dest_end.u64ptr,
Align_down((uint32_t)dest.u8ptr,
(uint32_t)FLASH_PAGE_SIZE) + FLASH_PAGE_SIZE);
/* Determine if the current destination page has an incomplete
* end. */
incomplete_flash_page_end
= (Align_down((uint32_t)dest.u8ptr,
(uint32_t)FLASH_PAGE_SIZE)
>= Align_down((uint32_t)dest_end.u8ptr,
(uint32_t)FLASH_PAGE_SIZE));
/* Use a flash double-word buffer to manage unaligned accesses. */
flash_dword.u64 = source.u64;
/* If destination does not point to the beginning of the current
* flash page... */
if (!Test_align((uint32_t)dest.u8ptr, FLASH_PAGE_SIZE)) {
/* Fill the beginning of the page buffer with the
* current flash page data.
* This is required by the hardware, even if page erase
* is not requested, in order to be able to write
* successfully to erased parts of flash pages that have
* already been written to. */
for (tmp.u8ptr = (uint8_t *)Align_down((uint32_t)dest.u8ptr,
(uint32_t)FLASH_PAGE_SIZE);
tmp.u64ptr < (uint64_t *)Align_down(
(uint32_t)dest.u8ptr,
sizeof(uint64_t));
tmp.u64ptr++) {
*tmp.u32ptr = *tmp.u32ptr;
*(tmp.u32ptr + 1) = *(tmp.u32ptr + 1);
}
/* If destination is not 64-bit aligned... */
if (!Test_align((uint32_t)dest.u8ptr,
sizeof(uint64_t))) {
/* Fill the beginning of the flash double-word
* buffer with the current flash page data.
* This is required by the hardware, even if
* page erase is not requested, in order to be
* able to write successfully to erased parts
* of flash pages that have already been written
* to. */
for (i = 0; i < Get_align((uint32_t)dest.u8ptr,
sizeof(uint64_t)); i++) {
flash_dword.u8[i] = *tmp.u8ptr++;
}
/* Align the destination pointer with its 64-bit
* boundary. */
dest.u64ptr = (uint64_t *)Align_down(
(uint32_t)dest.u8ptr,
sizeof(uint64_t));
/* If the current destination double-word is not
* the last one... */
if (dest.u64ptr < dest_end.u64ptr) {
/* Write the flash double-word buffer to
the page buffer and reinitialize it. */
*dest.u32ptr++ = flash_dword.u32[0];
*dest.u32ptr++ = flash_dword.u32[1];
flash_dword.u64 = source.u64;
}
}
}
/* Write the source data to the page buffer with 64-bit
* alignment. */
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--) {
*dest.u32ptr++ = source.u32[0];
*dest.u32ptr++ = source.u32[1];
}
/* If the current destination page has an incomplete end... */
if (incomplete_flash_page_end) {
/* This is required by the hardware, even if page erase
* is not requested, in order to be able to write
* successfully to erased parts of flash pages that have
* already been written to. */
{
tmp.u8ptr = (volatile uint8_t *)dest_end.u8ptr;
/* If end of destination is not 64-bit aligned. */
if (!Test_align((uint32_t)dest_end.u8ptr,
sizeof(uint64_t))) {
/* Fill the end of the flash double-word
* buffer with the current flash page
* data. */
for (i = Get_align(
(uint32_t)dest_end.u8ptr,
sizeof(uint64_t));
i < sizeof(uint64_t);
i++) {
flash_dword.u8[i] = *tmp.u8ptr++;
}
/* Write the flash double-word buffer to
* the page buffer. */
*dest.u32ptr++ = flash_dword.u32[0];
*dest.u32ptr++ = flash_dword.u32[1];
}
/* Fill the end of the page buffer with the
current flash page data. */
for (; !Test_align((uint32_t)tmp.u64ptr,
FLASH_PAGE_SIZE); tmp.u64ptr++){
*tmp.u32ptr = *tmp.u32ptr;
*(tmp.u32ptr + 1) = *(tmp.u32ptr + 1);
}
}
}
/* If the current flash page is in the flash array... */
if (dest.u8ptr <= (uint8_t *)FLASH_USER_PAGE_ADDR) {
/* Erase the current page if requested and write it from
* the page buffer. */
if (erase) {
flashcalw_erase_page(-1, false);
error_status |= flashcalw_error_status;
}
flashcalw_write_page(-1);
error_status |= flashcalw_error_status;
/* If the end of the flash array is reached, go to the
* User page. */
if (dest.u8ptr >= flash_array_end.u8ptr) {
dest.u8ptr = (uint8_t *)FLASH_USER_PAGE_ADDR;
}
} else {
/* Erase the User page if requested and write it from
* the page buffer. */
if (erase) {
flashcalw_erase_user_page(false);
error_status |= flashcalw_error_status;
}
flashcalw_write_user_page();
error_status |= flashcalw_error_status;
}
}
/* Update the FLASHC error status. */
flashcalw_error_status = error_status;
/* Return the initial destination pointer as the standard memset
* function does. */
return dst;
}
/*! \brief Copies \a nbytes bytes to the flash destination pointed to by \a dst
* from the source pointed to by \a src.
*
* The destination areas that are not within the flash
* array or the User page are caught by an Assert() operation.
*
* All pointer and size alignments are supported.
*
* \param dst Pointer to flash destination.
* \param src Pointer to source data.
* \param nbytes Number of bytes to copy.
* \param erase Whether to erase before writing: \c true or \c false.
*
* \return The value of \a dst.
*
* \warning If copying takes place between areas that overlap, the behavior is
* undefined.
*
* \warning This function may be called with \a erase set to \c false only if
* the destination consists only of erased words, i.e. this function
* can not be used to write only one bit of a previously written word.
* E.g., if \c 0x00000001 then \c 0xFFFFFFFE are written to a word, the
* resulting value in flash may be different from \c 0x00000000.
*
* \warning A Lock Error is issued if the command is applied to pages belonging
* to a locked region or to the bootloader protected area.
*
* \note The FLASHCALW error status returned by \ref flashcalw_is_lock_error and
* \ref flashcalw_is_programming_error is updated.
*/
volatile void *flashcalw_memcpy(volatile void *dst, const void *src,
size_t nbytes, bool erase)
{
uint16_t page_pos;
Union64 flash_dword;
uint8_t i;
bool b_user_page;
uint32_t error_status = 0;
volatile uint8_t *flash_add;
volatile uint8_t *dest_add = (uint8_t *)dst;
const uint8_t *src_buf = (const uint8_t *)src;
/* Copy area must be in flash array or flash user page */
Assert((((uint8_t *)dst >= (uint8_t *)FLASH_ADDR) &&
(((uint8_t *)dst + nbytes)
<= (((uint8_t *)FLASH_ADDR)
+ flashcalw_get_flash_size()))) ||
(((uint8_t *)dst >= (uint8_t *)FLASH_USER_PAGE_ADDR) &&
(((uint8_t *)dst + nbytes)
<= (((uint8_t *)FLASH_USER_PAGE_ADDR)
+ FLASH_PAGE_SIZE))));
b_user_page = (volatile uint8_t *)dst
>= (uint8_t *)FLASH_USER_PAGE_ADDR;
flash_add = (uint8_t *)((uint32_t)dest_add
- ((uint32_t)dest_add % FLASH_PAGE_SIZE));
while (nbytes) {
/* Clear the page buffer in order to prepare data for a flash
* page write. */
flashcalw_clear_page_buffer();
error_status |= flashcalw_error_status;
/* Loop in the page */
for (page_pos = 0; page_pos < FLASH_PAGE_SIZE;
page_pos += sizeof(uint64_t)) {
/* Read the flash double-word buffer */
flash_dword.u64 = *(volatile uint64_t *)flash_add;
/* Update double-word if necessary */
for (i = 0; i < sizeof(uint64_t); i++) {
if (nbytes && (flash_add == dest_add)) {
/* Update page with data source */
flash_dword.u8[i] = *src_buf++;
dest_add++;
nbytes--;
}
flash_add++;
}
/* Write the flash double-word buffer to the page buffer */
*(volatile uint64_t *)((uint32_t)flash_add
- sizeof(uint64_t)) = flash_dword.u64;
}
/* Erase the current page if requested and write it from the
* page buffer. */
if (erase) {
(b_user_page) ? flashcalw_erase_user_page(false)
: flashcalw_erase_page(-1, false);
error_status |= flashcalw_error_status;
}
/* Write the page */
(b_user_page) ? flashcalw_write_user_page()
: flashcalw_write_page(-1);
error_status |= flashcalw_error_status;
}
/* Update the FLASHC error status. */
flashcalw_error_status = error_status;
/* Return the initial destination pointer as the standard memcpy
* function does. */
return dst;
}
/** @} */
/*! \name PicoCache interfaces
*/
//! @{
/**
* \brief Enable PicoCache.
*/
void flashcalw_picocache_enable(void)
{
sysclk_enable_peripheral_clock(HCACHE);
HCACHE->HCACHE_CTRL = HCACHE_CTRL_CEN_YES;
while (!(flashcalw_picocache_get_status() & HCACHE_SR_CSTS_EN)) {
}
}
/**
* \brief Disable PicoCache.
*/
void flashcalw_picocache_disable(void)
{
HCACHE->HCACHE_MAINT0 = HCACHE_MAINT0_INVALL_YES;
HCACHE->HCACHE_CTRL = HCACHE_CTRL_CEN_NO;
while (flashcalw_picocache_get_status() != HCACHE_SR_CSTS_DIS) {
}
sysclk_disable_peripheral_clock(HCACHE);
}
/**
* \brief Get PicoCache status.
*
* \return 1 If PicoCahe is enabled, else disabled.
*/
uint32_t flashcalw_picocache_get_status(void)
{
return HCACHE->HCACHE_SR;
}
/**
* \brief Invalid all PicoCache lines.
*/
void flashcalw_picocache_invalid_all(void)
{
HCACHE->HCACHE_MAINT0 = HCACHE_MAINT0_INVALL_YES;
}
/**
* \brief Invalid specific cache line.
*
* \param index Lines to be invalid.
*/
void flashcalw_picocache_invalid_line(uint32_t index)
{
/* Disable the cache controller */
HCACHE->HCACHE_CTRL = HCACHE_CTRL_CEN_NO;
/* Wait for disable successfully */
while (flashcalw_picocache_get_status() != HCACHE_SR_CSTS_DIS) {
}
/* Invalid the line */
HCACHE->HCACHE_MAINT1 = index;
/* Enable the cache again */
HCACHE->HCACHE_CTRL = HCACHE_CTRL_CEN_YES;
}
/**
* \brief Set PicoCache monitor mode.
*
* \param mode PicoCache mode, 0 for cycle, 1 for instruction hit and 2 for data
*hit.
*/
void flashcalw_picocache_set_monitor_mode(uint32_t mode)
{
HCACHE->HCACHE_MCFG = mode;
}
/**
* \brief Enable PicoCache monitor.
*/
void flashcalw_picocache_enable_monitor(void)
{
HCACHE->HCACHE_MEN = HCACHE_MEN_MENABLE_EN;
}
/**
* \brief Disable PicoCache monitor.
*/
void flashcalw_picocache_disable_monitor(void)
{
HCACHE->HCACHE_MEN = HCACHE_MEN_MENABLE_DIS;
}
/**
* \brief Reset PicoCache monitor.
*/
void flashcalw_picocache_reset_monitor( void )
{
HCACHE->HCACHE_MCTRL = HCACHE_MCTRL_SWRST_YES;
}
/**
* \brief Get PicoCache monitor count number.
*
* \return Monitor counter number.
*/
uint32_t flashcalw_picocache_get_monitor_cnt( void )
{
return HCACHE->HCACHE_MSR;
}
/**
* \brief Get PicoCache version.
*
* \return PicoCache version.
*/
uint32_t flashcalw_picocache_get_version( void )
{
return HCACHE->HCACHE_VERSION;
}
/** @} */
/**
* \}
*/
/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
}
#endif
/**INDENT-ON**/
/// @endcond