/** | |
* \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 |