440 lines
16 KiB
C
440 lines
16 KiB
C
//*****************************************************************************
|
|
//
|
|
// am_hal_flash.c
|
|
//! @file
|
|
//!
|
|
//! @brief Functions for performing Flash operations.
|
|
//!
|
|
//! @addtogroup flash1 Flash
|
|
//! @ingroup apollo1hal
|
|
//! @{
|
|
//
|
|
//*****************************************************************************
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Copyright (c) 2020, Ambiq Micro
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// 1. 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. Neither the name of the copyright holder nor the names of its
|
|
// contributors may be used to endorse or promote products derived from this
|
|
// software without specific prior written permission.
|
|
//
|
|
// Third party software included in this distribution is subject to the
|
|
// additional license terms as defined in the /docs/licenses directory.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "am_mcu_apollo.h"
|
|
|
|
//
|
|
// Look-up table
|
|
//
|
|
const g_am_hal_flash_t g_am_hal_flash =
|
|
{
|
|
// am_hal_flash_mass_erase
|
|
((int (*)(uint32_t, uint32_t)) 0x0800004d),
|
|
// am_hal_flash_page_erase
|
|
((int (*)(uint32_t, uint32_t, uint32_t)) 0x08000051),
|
|
// am_hal_flash_program_main
|
|
((int (*)(uint32_t, const uint32_t *, uint32_t *, uint32_t)) 0x08000055),
|
|
// am_hal_flash_program_otp
|
|
((int (*)(uint32_t, uint32_t, const uint32_t *, uint32_t, uint32_t)) 0x08000059),
|
|
// am_hal_flash_program_main_sram
|
|
((void (*)(void)) 0x0800005d),
|
|
// am_hal_flash_program_otp_sram
|
|
((void (*)(void)) 0x08000061),
|
|
// am_hal_flash_erase_main_pages_sram
|
|
((void (*)(void)) 0x08000065),
|
|
// am_hal_flash_mass_erase_sram
|
|
((void (*)(void)) 0x08000069)
|
|
};
|
|
|
|
//
|
|
// Set up a very small function that will be guaranteed to be located in SRAM
|
|
// which can be used to to retrieve data from OTP.
|
|
// Make sure the function is word-aligned.
|
|
//
|
|
uint32_t SRAM_load_ui32[8 / 4] =
|
|
{
|
|
0xBF006800, // 6800 ldr r0,[r0,#0]
|
|
// BF00 nop
|
|
0xBF004770 // 4770 bx lr
|
|
// BF00 nop
|
|
};
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Implement an iterative spin loop.
|
|
//!
|
|
//! @param ui32Iterations - Number of iterations to delay.
|
|
//!
|
|
//! Use this function to implement a CPU busy waiting spin. For Apollo, this
|
|
//! delay can be used for timing purposes since for Apollo, each iteration will
|
|
//! take 3 cycles.
|
|
//!
|
|
//! @return None.
|
|
//
|
|
//*****************************************************************************
|
|
#if (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION < 6000000)
|
|
__asm void
|
|
am_hal_flash_delay(uint32_t ui32Iterations)
|
|
{
|
|
SUBS R0, #1
|
|
BNE am_hal_flash_delay
|
|
BX LR
|
|
}
|
|
#elif (defined (__ARMCC_VERSION)) && (__ARMCC_VERSION >= 6000000)
|
|
void __attribute__((naked))
|
|
am_hal_flash_delay(uint32_t ui32Iterations)
|
|
{
|
|
__asm(" subs r0, #1\n"
|
|
" bne am_hal_flash_delay\n"
|
|
" bx lr");
|
|
}
|
|
#elif defined(__GNUC_STDC_INLINE__)
|
|
void __attribute__((naked))
|
|
am_hal_flash_delay(uint32_t ui32Iterations)
|
|
{
|
|
__asm(" subs r0, #1\n"
|
|
" bne am_hal_flash_delay\n"
|
|
" bx lr");
|
|
}
|
|
#elif defined(__IAR_SYSTEMS_ICC__)
|
|
void
|
|
am_hal_flash_delay(uint32_t ui32Iterations)
|
|
{
|
|
asm("SUBS R0, #1");
|
|
asm("BNE.N am_hal_flash_delay");
|
|
asm("BX LR");
|
|
}
|
|
#else
|
|
#error Compiler is unknown, please contact Ambiq support team
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Delays for a desired amount of cycles while also waiting for a
|
|
//! status change.
|
|
//!
|
|
//! @param ui32usMaxDelay - Maximum number of ~1uS delay loops.
|
|
//! @param ui32Address - Address of the register for the status change.
|
|
//! @param ui32Mask - Mask for the status change.
|
|
//! @param ui32Value - Target value for the status change.
|
|
//!
|
|
//! This function will delay for approximately the given number of microseconds
|
|
//! while checking for a status change, exiting when either the given time has
|
|
//! expired or the status change is detected.
|
|
//!
|
|
//! @returns 0 = timeout.
|
|
//! 1 = status change detected.
|
|
//
|
|
//*****************************************************************************
|
|
uint32_t
|
|
am_hal_flash_delay_status_change(uint32_t ui32usMaxDelay, uint32_t ui32Address,
|
|
uint32_t ui32Mask, uint32_t ui32Value)
|
|
{
|
|
while ( ui32usMaxDelay-- )
|
|
{
|
|
//
|
|
// Check the status
|
|
//
|
|
if ( ( AM_REGVAL(ui32Address) & ui32Mask ) == ui32Value )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Call the BOOTROM cycle function to delay for about 1 microsecond.
|
|
//
|
|
am_hal_flash_delay( FLASH_CYCLES_US(1) );
|
|
}
|
|
|
|
return 0;
|
|
} // am_hal_flash_delay_status_change()
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function performs a mass erase on a flash block.
|
|
//!
|
|
//! @param ui32Value - The flash program key.
|
|
//! @param ui32FlashBlk - The flash block to erase.
|
|
//!
|
|
//! This function will erase the desired block of flash.
|
|
//!
|
|
//! @note Each flash block contains a maximum of 256kB.
|
|
//!
|
|
//! @return 0 for success, non-zero for failure.
|
|
//
|
|
//*****************************************************************************
|
|
int
|
|
am_hal_flash_mass_erase(uint32_t ui32Value, uint32_t ui32FlashBlk)
|
|
{
|
|
return g_am_hal_flash.am_hal_flash_mass_erase(ui32Value, ui32FlashBlk);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function performs a page erase on a flash block.
|
|
//!
|
|
//! @param ui32Value - The flash program key.
|
|
//! @param ui32FlashBlk - The flash block to reference the page number with.
|
|
//! @param ui32PageNum - The flash page offset into the selected block.
|
|
//!
|
|
//! This function will erase the desired flash page in the desired block of
|
|
//! flash.
|
|
//!
|
|
//! @note For Apollo, each flash page is 2KB (or AM_HAL_FLASH_PAGE_SIZE).
|
|
//! Each flash block (instance) contains a maximum of 128 pages
|
|
//! (or AM_HAL_FLASH_BLOCK_PAGES).
|
|
//!
|
|
//! @note When given an absolute flash address, a couple of helpful macros can
|
|
//! be utilized when calling this function.
|
|
//! For example:
|
|
//! am_hal_flash_page_erase(AM_HAL_FLASH_PROGRAM_KEY,
|
|
//! AM_HAL_FLASH_ADDR2BLOCK(ui32Addr),
|
|
//! AM_HAL_FLASH_ADDR2PAGE(ui32Addr) );
|
|
//!
|
|
//! @return 0 for success, non-zero for failure.
|
|
//
|
|
//*****************************************************************************
|
|
int
|
|
am_hal_flash_page_erase(uint32_t ui32Value, uint32_t ui32FlashBlk,
|
|
uint32_t ui32PageNum)
|
|
{
|
|
return g_am_hal_flash.am_hal_flash_page_erase(ui32Value, ui32FlashBlk,
|
|
ui32PageNum);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This programs up to N bytes of the Main array on one flash block.
|
|
//!
|
|
//! @param ui32Value - The Program key.
|
|
//! @param pui32Src - Pointer to word aligned array of data to program into
|
|
//! the flash block.
|
|
//! @param pui32Dst - Pointer to word aligned location to to begin programming
|
|
//! the flash block.
|
|
//! @param ui32NumWords - The Number of words to program.
|
|
//!
|
|
//! This function will program multiple words in the OTP block on flash
|
|
//! block (instance) 0.
|
|
//!
|
|
//! @return 0 for success, non-zero for failure.
|
|
//
|
|
//*****************************************************************************
|
|
int
|
|
am_hal_flash_program_main(uint32_t ui32Value, const uint32_t *pui32Src,
|
|
uint32_t *pui32Dst, uint32_t ui32NumWords)
|
|
{
|
|
return g_am_hal_flash.am_hal_flash_program_main(ui32Value, pui32Src,
|
|
pui32Dst, ui32NumWords);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function programs multiple words in the OTP.
|
|
//!
|
|
//! @param ui32Value - The OTP key.
|
|
//! @param ui32FlashBlk - The flash block where OTP lives (forced to block 0)
|
|
//! @param *pui32Src - Pointer to word aligned array of data to program into
|
|
//! the OTP block.
|
|
//! @param ui32Offset - Word offset into OTP (offset of 0 is the first word).
|
|
//! @param ui32NumWords - The Number of words to program.
|
|
//!
|
|
//! This function will program multiple words in the OTP block on flash
|
|
//! block (instance) 0.
|
|
//!
|
|
//! @note Only the upper half of the OTP block can be written
|
|
//! so 1 < ui32NumWords < 257.
|
|
//!
|
|
//! @return 0 for success, non-zero for failure.
|
|
//
|
|
//*****************************************************************************
|
|
int
|
|
am_hal_flash_program_otp(uint32_t ui32Value, uint32_t ui32FlashBlk,
|
|
const uint32_t *pui32Src, uint32_t ui32Offset,
|
|
uint32_t ui32NumWords)
|
|
{
|
|
return g_am_hal_flash.am_hal_flash_program_otp(ui32Value, 0, pui32Src,
|
|
ui32Offset, ui32NumWords);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function programs multiple words in the OTP using SRAM as args.
|
|
//!
|
|
//! This function will program multiple words in the OTP block using SRAM as
|
|
//! its arguments. This is helpful for tools/manufacturing.
|
|
//!
|
|
//! The SRAM addresses of interest are:
|
|
//!
|
|
//! 0x10000000 Offset in to OTP block, 0 <= Offset < 256
|
|
//! 0x10000004 Number of 32-bit words to program
|
|
//! 0x10000008 OTP key
|
|
//! 0x1000000C Debugger sets this to -1 and all return codes are >= 0
|
|
//! 0x10000010 First 32-bit word of data buffer to be programmed
|
|
//!
|
|
//! @note This routine spins when am_hal_flash_program_otp() returns and waits
|
|
//! for the debugger surrogate in the parallel programmer.
|
|
//!
|
|
//! @return never returns, spins here waiting for debugger or debugger surrogate
|
|
//! on the parallel programmer.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
am_hal_flash_program_otp_sram(void)
|
|
{
|
|
g_am_hal_flash.am_hal_flash_program_otp_sram();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function erases pages in the main array using SRAM as args.
|
|
//!
|
|
//! This function will erase multiple pages in the flash main array using SRAM
|
|
//! as its arguments. This is helpful for tools/manufacturing.
|
|
//!
|
|
//! The SRAM addresses of interest are:
|
|
//!
|
|
//! 0x10000000 Flash block/instance number
|
|
//! 0x10000004 Number of pages to erase (must be between 1 and 128
|
|
//! inclusive)
|
|
//! 0x10000008 PROGRAM key
|
|
//! 0x1000000C Debugger sets this to -1 and all return codes are >= 0
|
|
//! 0x10000010 Page number of the first flash page to erase. NOTE: these
|
|
//! *HAVE* to be sequential (range 0 <= PageNumber <= 127)
|
|
//!
|
|
//! @note This routine spins when am_hal_flash_page_erase() returns and waits
|
|
//! for the debugger surrogate in the parallel programmer. Before spinning it
|
|
//! unconditional executes a break point instruction.
|
|
//!
|
|
//! @return never returns, spins here waiting for debugger or debugger surrogate
|
|
//! on the parallel programmer.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
am_hal_flash_erase_main_pages_sram(void)
|
|
{
|
|
g_am_hal_flash.am_hal_flash_erase_main_pages_sram();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function mass erases a flash block with SRAM as args.
|
|
//!
|
|
//! This function will perform a mass erase on a flash block using SRAM as its
|
|
//! arguments. This is helpful for tools/manufacturing.
|
|
//!
|
|
//! The SRAM addresses of interest are:
|
|
//!
|
|
//! 0x10000000 Pointer in to flash block.
|
|
//! 0x10000004 PROGRAM key
|
|
//! 0x10000008 Return code (Debugger sets this to -1 and all return
|
|
//! codes are >= 0)
|
|
//!
|
|
//! @note This routine spins when am_hal_flash_mass_erase() returns and waits
|
|
//! for the debugger surrogate in the parallel programmer.
|
|
//!
|
|
//! @return never returns, spins here waiting for debugger or debugger surrogate
|
|
//! on the parallel programmer.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
am_hal_flash_mass_erase_sram(void)
|
|
{
|
|
g_am_hal_flash.am_hal_flash_mass_erase_sram();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief This function programs the main array using SRAM as args.
|
|
//!
|
|
//! This function will program the main array using SRAM as it arguments. This
|
|
//! is helpful for tools/manufacturing.
|
|
//!
|
|
//! The SRAM addresses of interest are:
|
|
//!
|
|
//! 0x10000000 Pointer in to flash
|
|
//! 0x10000004 Number of 32-bit words to program
|
|
//! 0x10000008 PROGRAM key.
|
|
//! 0x1000000C Debugger sets this to -1 and all return codes are >= 0
|
|
//! 0x10000010 First 32-bit word of data buffer to be programmed
|
|
//!
|
|
//! @note This routine spins when am_hal_flash_program_main() returns and waits
|
|
//! for the debugger surrogate in the parallel programmer. Before spinning it
|
|
//! unconditionally executes a break point instruction.
|
|
//!
|
|
//! @return never returns, spins here waiting for debugger or debugger surrogate
|
|
//! on the parallel programmer.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
am_hal_flash_program_main_sram(void)
|
|
{
|
|
g_am_hal_flash.am_hal_flash_program_main_sram();
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Return ui32 value obtained from anywhere in D Code or System Bus
|
|
//!
|
|
//! @param ui32Address - return the value corresponding to this location in OTP
|
|
//!
|
|
//! Use this function to read a value from various peripheral locations
|
|
//! that must be read from code running in SRAM.
|
|
//!
|
|
//! This function is not required to reside in SRAM as it calls a small
|
|
//! function that does the actual read which is guaranteed to be in SRAM.
|
|
//!
|
|
//! @return the value found
|
|
//
|
|
//*****************************************************************************
|
|
uint32_t
|
|
am_hal_flash_load_ui32(uint32_t ui32Address)
|
|
{
|
|
//
|
|
// Call the simple routine that has been set up in SRAM as an array.
|
|
// First set up a function pointer to the array, being sure to set the
|
|
// .T bit (Thumb bit, bit0) in the branch address, then use that
|
|
// function ptr to call the SRAM function.
|
|
//
|
|
uint32_t SRAMCode = (uint32_t)SRAM_load_ui32 | 0x1;
|
|
uint32_t (*pFunc)(uint32_t) = (uint32_t (*)(uint32_t))SRAMCode;
|
|
return (*pFunc)(ui32Address);
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// End Doxygen group.
|
|
//! @}
|
|
//
|
|
//*****************************************************************************
|