OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [freertos-6.1.1/] [Demo/] [lwIP_AVR32_UC3/] [DRIVERS/] [FLASHC/] [flashc.c] - Rev 666

Go to most recent revision | Compare with Previous | Blame | View Log

/*This file is prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief FLASHC driver for AVR32 UC3.
 *
 * AVR32 Flash Controller driver module.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices with a FLASHC module can be used.
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 ******************************************************************************/
 
/* Copyright (c) 2007, Atmel Corporation 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. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``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 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.
 */
 
 
#include <avr32/io.h>
#include <stddef.h>
#include "compiler.h"
#include "flashc.h"
 
 
/*! \name FLASHC Writable Bit-Field Registers
 */
//! @{
 
typedef union
{
  unsigned long                 fcr;
  avr32_flashc_fcr_t            FCR;
} u_avr32_flashc_fcr_t;
 
typedef union
{
  unsigned long                 fcmd;
  avr32_flashc_fcmd_t           FCMD;
} u_avr32_flashc_fcmd_t;
 
//! @}
 
 
/*! \name Flash Properties
 */
//! @{
 
 
unsigned int flashc_get_flash_size(void)
{
  static const unsigned int FLASH_SIZE[1 << AVR32_FLASHC_FSR_FSZ_SIZE] =
  {
      32 << 10,
      64 << 10,
     128 << 10,
     256 << 10,
     384 << 10,
     512 << 10,
     768 << 10,
    1024 << 10
  };
  return FLASH_SIZE[(AVR32_FLASHC.fsr & AVR32_FLASHC_FSR_FSZ_MASK) >> AVR32_FLASHC_FSR_FSZ_OFFSET];
}
 
 
unsigned int flashc_get_page_count(void)
{
  return flashc_get_flash_size() / AVR32_FLASHC_PAGE_SIZE;
}
 
 
unsigned int flashc_get_page_count_per_region(void)
{
  return flashc_get_page_count() / AVR32_FLASHC_REGIONS;
}
 
 
unsigned int flashc_get_page_region(int page_number)
{
  return ((page_number >= 0) ? page_number : flashc_get_page_number()) / flashc_get_page_count_per_region();
}
 
 
unsigned int flashc_get_region_first_page_number(unsigned int region)
{
  return region * flashc_get_page_count_per_region();
}
 
 
//! @}
 
 
/*! \name FLASHC Control
 */
//! @{
 
 
unsigned int flashc_get_wait_state(void)
{
  return (AVR32_FLASHC.fcr & AVR32_FLASHC_FCR_FWS_MASK) >> AVR32_FLASHC_FCR_FWS_OFFSET;
}
 
 
void flashc_set_wait_state(unsigned int wait_state)
{
  u_avr32_flashc_fcr_t u_avr32_flashc_fcr = {AVR32_FLASHC.fcr};
  u_avr32_flashc_fcr.FCR.fws = wait_state;
  AVR32_FLASHC.fcr = u_avr32_flashc_fcr.fcr;
}
 
 
Bool flashc_is_ready_int_enabled(void)
{
  return ((AVR32_FLASHC.fcr & AVR32_FLASHC_FCR_FRDY_MASK) != 0);
}
 
 
void flashc_enable_ready_int(Bool enable)
{
  u_avr32_flashc_fcr_t u_avr32_flashc_fcr = {AVR32_FLASHC.fcr};
  u_avr32_flashc_fcr.FCR.frdy = (enable != FALSE);
  AVR32_FLASHC.fcr = u_avr32_flashc_fcr.fcr;
}
 
 
Bool flashc_is_lock_error_int_enabled(void)
{
  return ((AVR32_FLASHC.fcr & AVR32_FLASHC_FCR_LOCKE_MASK) != 0);
}
 
 
void flashc_enable_lock_error_int(Bool enable)
{
  u_avr32_flashc_fcr_t u_avr32_flashc_fcr = {AVR32_FLASHC.fcr};
  u_avr32_flashc_fcr.FCR.locke = (enable != FALSE);
  AVR32_FLASHC.fcr = u_avr32_flashc_fcr.fcr;
}
 
 
Bool flashc_is_prog_error_int_enabled(void)
{
  return ((AVR32_FLASHC.fcr & AVR32_FLASHC_FCR_PROGE_MASK) != 0);
}
 
 
void flashc_enable_prog_error_int(Bool enable)
{
  u_avr32_flashc_fcr_t u_avr32_flashc_fcr = {AVR32_FLASHC.fcr};
  u_avr32_flashc_fcr.FCR.proge = (enable != FALSE);
  AVR32_FLASHC.fcr = u_avr32_flashc_fcr.fcr;
}
 
 
//! @}
 
 
/*! \name FLASHC Status
 */
//! @{
 
 
Bool flashc_is_ready(void)
{
  return ((AVR32_FLASHC.fsr & AVR32_FLASHC_FSR_FRDY_MASK) != 0);
}
 
 
void flashc_default_wait_until_ready(void)
{
  while (!flashc_is_ready());
}
 
 
void (*volatile flashc_wait_until_ready)(void) = flashc_default_wait_until_ready;
 
 
/*! \brief Gets the error status of the FLASHC.
 *
 * \return The error status of the FLASHC built up from
 *         \c AVR32_FLASHC_FSR_LOCKE_MASK and \c AVR32_FLASHC_FSR_PROGE_MASK.
 *
 * \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 flashc_is_lock_error
 *          and \ref flashc_is_programming_error.
 */
static unsigned int flashc_get_error_status(void)
{
  return AVR32_FLASHC.fsr & (AVR32_FLASHC_FSR_LOCKE_MASK |
                             AVR32_FLASHC_FSR_PROGE_MASK);
}
 
 
//! Sticky error status of the FLASHC.
//! This variable is updated by functions that issue FLASHC commands. It
//! contains the cumulated FLASHC error status of all the FLASHC commands issued
//! by a function.
static unsigned int flashc_error_status = 0;
 
 
Bool flashc_is_lock_error(void)
{
  return ((flashc_error_status & AVR32_FLASHC_FSR_LOCKE_MASK) != 0);
}
 
 
Bool flashc_is_programming_error(void)
{
  return ((flashc_error_status & AVR32_FLASHC_FSR_PROGE_MASK) != 0);
}
 
 
//! @}
 
 
/*! \name FLASHC Command Control
 */
//! @{
 
 
unsigned int flashc_get_command(void)
{
  return (AVR32_FLASHC.fcmd & AVR32_FLASHC_FCMD_CMD_MASK) >> AVR32_FLASHC_FCMD_CMD_OFFSET;
}
 
 
unsigned int flashc_get_page_number(void)
{
  return (AVR32_FLASHC.fcmd & AVR32_FLASHC_FCMD_PAGEN_MASK) >> AVR32_FLASHC_FCMD_PAGEN_OFFSET;
}
 
 
void flashc_issue_command(unsigned int command, int page_number)
{
  u_avr32_flashc_fcmd_t u_avr32_flashc_fcmd;
  flashc_wait_until_ready();
  u_avr32_flashc_fcmd.fcmd = AVR32_FLASHC.fcmd;
  u_avr32_flashc_fcmd.FCMD.cmd = command;
  if (page_number >= 0) u_avr32_flashc_fcmd.FCMD.pagen = page_number;
  u_avr32_flashc_fcmd.FCMD.key = AVR32_FLASHC_FCMD_KEY_KEY;
  AVR32_FLASHC.fcmd = u_avr32_flashc_fcmd.fcmd;
  flashc_error_status = flashc_get_error_status();
  flashc_wait_until_ready();
}
 
 
//! @}
 
 
/*! \name FLASHC Global Commands
 */
//! @{
 
 
void flashc_no_operation(void)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_NOP, -1);
}
 
 
void flashc_erase_all(void)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_EA, -1);
}
 
 
//! @}
 
 
/*! \name FLASHC Protection Mechanisms
 */
//! @{
 
 
Bool flashc_is_security_bit_active(void)
{
  return ((AVR32_FLASHC.fsr & AVR32_FLASHC_FSR_SECURITY_MASK) != 0);
}
 
 
void flashc_activate_security_bit(void)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_SSB, -1);
}
 
 
unsigned int flashc_get_bootloader_protected_size(void)
{
  unsigned int bootprot = (1 << AVR32_FLASHC_FGPFR_BOOTPROT_SIZE) - 1 -
                          flashc_read_gp_fuse_bitfield(AVR32_FLASHC_FGPFR_BOOTPROT_OFFSET,
                                                       AVR32_FLASHC_FGPFR_BOOTPROT_SIZE);
  return (bootprot) ? AVR32_FLASHC_PAGE_SIZE << bootprot : 0;
}
 
 
unsigned int flashc_set_bootloader_protected_size(unsigned int bootprot_size)
{
  flashc_set_gp_fuse_bitfield(AVR32_FLASHC_FGPFR_BOOTPROT_OFFSET,
                              AVR32_FLASHC_FGPFR_BOOTPROT_SIZE,
                              (1 << AVR32_FLASHC_FGPFR_BOOTPROT_SIZE) - 1 -
                              ((bootprot_size) ?
                               32 - clz((((min(max(bootprot_size, AVR32_FLASHC_PAGE_SIZE << 1),
                                               AVR32_FLASHC_PAGE_SIZE <<
                                               ((1 << AVR32_FLASHC_FGPFR_BOOTPROT_SIZE) - 1)) +
                                           AVR32_FLASHC_PAGE_SIZE - 1) /
                                          AVR32_FLASHC_PAGE_SIZE) << 1) - 1) - 1 :
                               0));
  return flashc_get_bootloader_protected_size();
}
 
 
Bool flashc_is_external_privileged_fetch_locked(void)
{
  return (!flashc_read_gp_fuse_bit(AVR32_FLASHC_FGPFR_EPFL_OFFSET));
}
 
 
void flashc_lock_external_privileged_fetch(Bool lock)
{
  flashc_set_gp_fuse_bit(AVR32_FLASHC_FGPFR_EPFL_OFFSET, !lock);
}
 
 
Bool flashc_is_page_region_locked(int page_number)
{
  return flashc_is_region_locked(flashc_get_page_region(page_number));
}
 
 
Bool flashc_is_region_locked(unsigned int region)
{
  return ((AVR32_FLASHC.fsr & AVR32_FLASHC_FSR_LOCK0_MASK << (region & (AVR32_FLASHC_REGIONS - 1))) != 0);
}
 
 
void flashc_lock_page_region(int page_number, Bool lock)
{
  flashc_issue_command((lock) ? AVR32_FLASHC_FCMD_CMD_LP : AVR32_FLASHC_FCMD_CMD_UP, page_number);
}
 
 
void flashc_lock_region(unsigned int region, Bool lock)
{
  flashc_lock_page_region(flashc_get_region_first_page_number(region), lock);
}
 
 
void flashc_lock_all_regions(Bool lock)
{
  unsigned int error_status = 0;
  unsigned int region = AVR32_FLASHC_REGIONS;
  while (region)
  {
    flashc_lock_region(--region, lock);
    error_status |= flashc_error_status;
  }
  flashc_error_status = error_status;
}
 
 
//! @}
 
 
/*! \name Access to General-Purpose Fuses
 */
//! @{
 
 
Bool flashc_read_gp_fuse_bit(unsigned int gp_fuse_bit)
{
  return ((AVR32_FLASHC.fgpfr & AVR32_FLASHC_FGPFR_GPF00_MASK << (gp_fuse_bit & 0x1F)) != 0);
}
 
 
U32 flashc_read_gp_fuse_bitfield(unsigned int pos, unsigned int width)
{
  return AVR32_FLASHC.fgpfr >> (AVR32_FLASHC_FGPFR_GPF00_OFFSET + (pos & 0x1F)) &
         ((1 << min(width, 32)) - 1);
}
 
 
U8 flashc_read_gp_fuse_byte(unsigned int gp_fuse_byte)
{
  return AVR32_FLASHC.fgpfr >> (AVR32_FLASHC_FGPFR_GPF00_OFFSET + ((gp_fuse_byte & 0x03) << 3));
}
 
 
U32 flashc_read_all_gp_fuses(void)
{
  return AVR32_FLASHC.fgpfr;
}
 
 
Bool flashc_erase_gp_fuse_bit(unsigned int gp_fuse_bit, Bool check)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_EGPB, gp_fuse_bit & 0x1F);
  return (check) ? flashc_read_gp_fuse_bit(gp_fuse_bit) : TRUE;
}
 
 
Bool flashc_erase_gp_fuse_bitfield(unsigned int pos, unsigned int width, Bool check)
{
  unsigned int error_status = 0;
  unsigned int gp_fuse_bit;
  pos &= 0x1F;
  width = min(width, 32);
  for (gp_fuse_bit = pos; gp_fuse_bit < pos + width; gp_fuse_bit++)
  {
    flashc_erase_gp_fuse_bit(gp_fuse_bit, FALSE);
    error_status |= flashc_error_status;
  }
  flashc_error_status = error_status;
  return (check) ? (flashc_read_gp_fuse_bitfield(pos, width) == (1 << width) - 1) : TRUE;
}
 
 
Bool flashc_erase_gp_fuse_byte(unsigned int gp_fuse_byte, Bool check)
{
  unsigned int error_status;
  unsigned int current_gp_fuse_byte;
  U32 value = flashc_read_all_gp_fuses();
  flashc_erase_all_gp_fuses(FALSE);
  error_status = flashc_error_status;
  for (current_gp_fuse_byte = 0; current_gp_fuse_byte < 4; current_gp_fuse_byte++, value >>= 8)
  {
    if (current_gp_fuse_byte != gp_fuse_byte)
    {
      flashc_write_gp_fuse_byte(current_gp_fuse_byte, value);
      error_status |= flashc_error_status;
    }
  }
  flashc_error_status = error_status;
  return (check) ? (flashc_read_gp_fuse_byte(gp_fuse_byte) == 0xFF) : TRUE;
}
 
 
Bool flashc_erase_all_gp_fuses(Bool check)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_EAGPF, -1);
  return (check) ? (flashc_read_all_gp_fuses() == 0xFFFFFFFF) : TRUE;
}
 
 
void flashc_write_gp_fuse_bit(unsigned int gp_fuse_bit, Bool value)
{
  if (!value)
    flashc_issue_command(AVR32_FLASHC_FCMD_CMD_WGPB, gp_fuse_bit & 0x1F);
}
 
 
void flashc_write_gp_fuse_bitfield(unsigned int pos, unsigned int width, U32 value)
{
  unsigned int error_status = 0;
  unsigned int gp_fuse_bit;
  pos &= 0x1F;
  width = min(width, 32);
  for (gp_fuse_bit = pos; gp_fuse_bit < pos + width; gp_fuse_bit++, value >>= 1)
  {
    flashc_write_gp_fuse_bit(gp_fuse_bit, value & 0x01);
    error_status |= flashc_error_status;
  }
  flashc_error_status = error_status;
}
 
 
void flashc_write_gp_fuse_byte(unsigned int gp_fuse_byte, U8 value)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_PGPFB, (gp_fuse_byte & 0x03) | value << 2);
}
 
 
void flashc_write_all_gp_fuses(U32 value)
{
  unsigned int error_status = 0;
  unsigned int gp_fuse_byte;
  for (gp_fuse_byte = 0; gp_fuse_byte < 4; gp_fuse_byte++, value >>= 8)
  {
    flashc_write_gp_fuse_byte(gp_fuse_byte, value);
    error_status |= flashc_error_status;
  }
  flashc_error_status = error_status;
}
 
 
void flashc_set_gp_fuse_bit(unsigned int gp_fuse_bit, Bool value)
{
  if (value)
    flashc_erase_gp_fuse_bit(gp_fuse_bit, FALSE);
  else
    flashc_write_gp_fuse_bit(gp_fuse_bit, FALSE);
}
 
 
void flashc_set_gp_fuse_bitfield(unsigned int pos, unsigned int width, U32 value)
{
  unsigned int error_status = 0;
  unsigned int gp_fuse_bit;
  pos &= 0x1F;
  width = min(width, 32);
  for (gp_fuse_bit = pos; gp_fuse_bit < pos + width; gp_fuse_bit++, value >>= 1)
  {
    flashc_set_gp_fuse_bit(gp_fuse_bit, value & 0x01);
    error_status |= flashc_error_status;
  }
  flashc_error_status = error_status;
}
 
 
void flashc_set_gp_fuse_byte(unsigned int gp_fuse_byte, U8 value)
{
  unsigned int error_status;
  switch (value)
  {
  case 0xFF:
    flashc_erase_gp_fuse_byte(gp_fuse_byte, FALSE);
    break;
  case 0x00:
    flashc_write_gp_fuse_byte(gp_fuse_byte, 0x00);
    break;
  default:
    flashc_erase_gp_fuse_byte(gp_fuse_byte, FALSE);
    error_status = flashc_error_status;
    flashc_write_gp_fuse_byte(gp_fuse_byte, value);
    flashc_error_status |= error_status;
  }
}
 
 
void flashc_set_all_gp_fuses(U32 value)
{
  unsigned int error_status;
  switch (value)
  {
  case 0xFFFFFFFF:
    flashc_erase_all_gp_fuses(FALSE);
    break;
  case 0x00000000:
    flashc_write_all_gp_fuses(0x00000000);
    break;
  default:
    flashc_erase_all_gp_fuses(FALSE);
    error_status = flashc_error_status;
    flashc_write_all_gp_fuses(value);
    flashc_error_status |= error_status;
  }
}
 
 
//! @}
 
 
/*! \name Access to Flash Pages
 */
//! @{
 
 
void flashc_clear_page_buffer(void)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_CPB, -1);
}
 
 
Bool flashc_is_page_erased(void)
{
  return ((AVR32_FLASHC.fsr & AVR32_FLASHC_FSR_QPRR_MASK) != 0);
}
 
 
Bool flashc_quick_page_read(int page_number)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_QPR, page_number);
  return flashc_is_page_erased();
}
 
 
Bool flashc_erase_page(int page_number, Bool check)
{
  Bool page_erased = TRUE;
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_EP, page_number);
  if (check)
  {
    unsigned int error_status = flashc_error_status;
    page_erased = flashc_quick_page_read(-1);
    flashc_error_status |= error_status;
  }
  return page_erased;
}
 
 
Bool flashc_erase_all_pages(Bool check)
{
  Bool all_pages_erased = TRUE;
  unsigned int error_status = 0;
  unsigned int page_number = flashc_get_page_count();
  while (page_number)
  {
    all_pages_erased &= flashc_erase_page(--page_number, check);
    error_status |= flashc_error_status;
  }
  flashc_error_status = error_status;
  return all_pages_erased;
}
 
 
void flashc_write_page(int page_number)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_WP, page_number);
}
 
 
Bool flashc_check_user_page_erase(void)
{
  volatile U64 *user_page_ptr = (U64 *)AVR32_FLASHC_USER_PAGE;
  while (user_page_ptr < (U64 *)(AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE))
  {
    if (*user_page_ptr++ != 0xFFFFFFFFFFFFFFFFULL)
      return FALSE;
  }
  return TRUE;
}
 
 
Bool flashc_erase_user_page(Bool check)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_EUP, -1);
  return (check) ? flashc_check_user_page_erase() : TRUE;
}
 
 
void flashc_write_user_page(void)
{
  flashc_issue_command(AVR32_FLASHC_FCMD_CMD_WUP, -1);
}
 
 
volatile void *flashc_memset8(volatile void *dst, U8 src, size_t nbytes, Bool erase)
{
  return flashc_memset16(dst, src | (U16)src << 8, nbytes, erase);
}
 
 
volatile void *flashc_memset16(volatile void *dst, U16 src, size_t nbytes, Bool erase)
{
  return flashc_memset32(dst, src | (U32)src << 16, nbytes, erase);
}
 
 
volatile void *flashc_memset32(volatile void *dst, U32 src, size_t nbytes, Bool erase)
{
  return flashc_memset64(dst, src | (U64)src << 32, nbytes, erase);
}
 
 
volatile void *flashc_memset64(volatile void *dst, U64 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;
  unsigned int error_status = 0;
  unsigned int i;
 
  // Reformat arguments.
  flash_array_end.u8ptr = AVR32_FLASH + flashc_get_flash_size();
  dest.u8ptr = dst;
  for (i = (Get_align((U32)dest.u8ptr, sizeof(U64)) - 1) & (sizeof(U64) - 1);
       src; i = (i - 1) & (sizeof(U64) - 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 < AVR32_FLASH)
  {
    dest.u8ptr = AVR32_FLASH;
  }
  else if (flash_array_end.u8ptr <= dest.u8ptr && dest.u8ptr < AVR32_FLASHC_USER_PAGE)
  {
    dest.u8ptr = AVR32_FLASHC_USER_PAGE;
  }
 
  // If end of destination is outside flash, move it to the end of the previous flash page if any.
  if (dest_end.u8ptr > AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE)
  {
    dest_end.u8ptr = AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE;
  }
  else if (AVR32_FLASHC_USER_PAGE >= 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 = (U16 *)Align_down((U32)dest_end.u8ptr, sizeof(U16));
  dest_end.u32ptr = (U32 *)Align_down((U32)dest_end.u16ptr, sizeof(U32));
  dest_end.u64ptr = (U64 *)Align_down((U32)dest_end.u32ptr, sizeof(U64));
 
  // 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.
    flashc_clear_page_buffer();
    error_status |= flashc_error_status;
 
    // Determine where the source data will end in the current flash page.
    flash_page_source_end.u64ptr =
      (U64 *)min((U32)dest_end.u64ptr,
                 Align_down((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE) + AVR32_FLASHC_PAGE_SIZE);
 
    // Determine if the current destination page has an incomplete end.
    incomplete_flash_page_end = (Align_down((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE) >=
                                 Align_down((U32)dest_end.u8ptr, AVR32_FLASHC_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((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE))
    {
      // If page erase is requested...
      if (erase)
      {
        // Fill the beginning of the page buffer with the current flash page data.
        for (tmp.u8ptr = (U8 *)Align_down((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE);
             tmp.u64ptr < (U64 *)Align_down((U32)dest.u8ptr, sizeof(U64));
             tmp.u64ptr++)
          *tmp.u64ptr = *tmp.u64ptr;
      }
 
      // If destination is not 64-bit aligned...
      if (!Test_align((U32)dest.u8ptr, sizeof(U64)))
      {
        // If page erase is requested...
        if (erase)
        {
          // Fill the beginning of the flash double-word buffer with the current flash page data.
          for (i = 0; i < Get_align((U32)dest.u8ptr, sizeof(U64)); i++)
            flash_dword.u8[i] = *tmp.u8ptr++;
        }
        // If page erase is not requested...
        else
        {
          // Erase the beginning of the flash double-word buffer.
          for (i = 0; i < Get_align((U32)dest.u8ptr, sizeof(U64)); i++)
            flash_dword.u8[i] = 0xFF;
        }
 
        // Align the destination pointer with its 64-bit boundary.
        dest.u64ptr = (U64 *)Align_down((U32)dest.u8ptr, sizeof(U64));
 
        // 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.u64ptr++ = flash_dword.u64;
          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.u64ptr++ = source.u64;
 
    // If the current destination page has an incomplete end...
    if (incomplete_flash_page_end)
    {
      // If page erase is requested...
      if (erase)
      {
        tmp.u8ptr = (volatile U8 *)dest_end.u8ptr;
 
        // If end of destination is not 64-bit aligned...
        if (!Test_align((U32)dest_end.u8ptr, sizeof(U64)))
        {
          // Fill the end of the flash double-word buffer with the current flash page data.
          for (i = Get_align((U32)dest_end.u8ptr, sizeof(U64)); i < sizeof(U64); i++)
            flash_dword.u8[i] = *tmp.u8ptr++;
 
          // Write the flash double-word buffer to the page buffer.
          *dest.u64ptr++ = flash_dword.u64;
        }
 
        // Fill the end of the page buffer with the current flash page data.
        for (; !Test_align((U32)tmp.u64ptr, AVR32_FLASHC_PAGE_SIZE); tmp.u64ptr++)
          *tmp.u64ptr = *tmp.u64ptr;
      }
      // If page erase is not requested but end of destination is not 64-bit aligned...
      else if (!Test_align((U32)dest_end.u8ptr, sizeof(U64)))
      {
        // Erase the end of the flash double-word buffer.
        for (i = Get_align((U32)dest_end.u8ptr, sizeof(U64)); i < sizeof(U64); i++)
          flash_dword.u8[i] = 0xFF;
 
        // Write the flash double-word buffer to the page buffer.
        *dest.u64ptr++ = flash_dword.u64;
      }
    }
 
    // If the current flash page is in the flash array...
    if (dest.u8ptr <= AVR32_FLASHC_USER_PAGE)
    {
      // Erase the current page if requested and write it from the page buffer.
      if (erase)
      {
        flashc_erase_page(-1, FALSE);
        error_status |= flashc_error_status;
      }
      flashc_write_page(-1);
      error_status |= flashc_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 = AVR32_FLASHC_USER_PAGE;
    }
    // If the current flash page is the User page...
    else
    {
      // Erase the User page if requested and write it from the page buffer.
      if (erase)
      {
        flashc_erase_user_page(FALSE);
        error_status |= flashc_error_status;
      }
      flashc_write_user_page();
      error_status |= flashc_error_status;
    }
  }
 
  // Update the FLASHC error status.
  flashc_error_status = error_status;
 
  // Return the initial destination pointer as the standard memset function does.
  return dst;
}
 
 
volatile void *flashc_memcpy(volatile void *dst, const void *src, size_t nbytes, Bool erase)
{
  // Use aggregated pointers to have several alignments available for a same address.
  UnionCVPtr flash_array_end;
  UnionVPtr dest;
  UnionCPtr source;
  StructCVPtr dest_end;
  UnionCVPtr flash_page_source_end;
  Bool incomplete_flash_page_end;
  Union64 flash_dword;
  Bool flash_dword_pending = FALSE;
  UnionVPtr tmp;
  unsigned int error_status = 0;
  unsigned int i, j;
 
  // Reformat arguments.
  flash_array_end.u8ptr = AVR32_FLASH + flashc_get_flash_size();
  dest.u8ptr = dst;
  source.u8ptr = src;
  dest_end.u8ptr = dest.u8ptr + nbytes;
 
  // If destination is outside flash, go to next flash page if any.
  if (dest.u8ptr < AVR32_FLASH)
  {
    source.u8ptr += AVR32_FLASH - dest.u8ptr;
    dest.u8ptr = AVR32_FLASH;
  }
  else if (flash_array_end.u8ptr <= dest.u8ptr && dest.u8ptr < AVR32_FLASHC_USER_PAGE)
  {
    source.u8ptr += AVR32_FLASHC_USER_PAGE - dest.u8ptr;
    dest.u8ptr = AVR32_FLASHC_USER_PAGE;
  }
 
  // If end of destination is outside flash, move it to the end of the previous flash page if any.
  if (dest_end.u8ptr > AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE)
  {
    dest_end.u8ptr = AVR32_FLASHC_USER_PAGE + AVR32_FLASHC_USER_PAGE_SIZE;
  }
  else if (AVR32_FLASHC_USER_PAGE >= 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 = (U16 *)Align_down((U32)dest_end.u8ptr, sizeof(U16));
  dest_end.u32ptr = (U32 *)Align_down((U32)dest_end.u16ptr, sizeof(U32));
  dest_end.u64ptr = (U64 *)Align_down((U32)dest_end.u32ptr, sizeof(U64));
 
  // 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.
    flashc_clear_page_buffer();
    error_status |= flashc_error_status;
 
    // Determine where the source data will end in the current flash page.
    flash_page_source_end.u64ptr =
      (U64 *)min((U32)dest_end.u64ptr,
                 Align_down((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE) + AVR32_FLASHC_PAGE_SIZE);
 
    // Determine if the current destination page has an incomplete end.
    incomplete_flash_page_end = (Align_down((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE) >=
                                 Align_down((U32)dest_end.u8ptr, AVR32_FLASHC_PAGE_SIZE));
 
    // If destination does not point to the beginning of the current flash page...
    if (!Test_align((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE))
    {
      // If page erase is requested...
      if (erase)
      {
        // Fill the beginning of the page buffer with the current flash page data.
        for (tmp.u8ptr = (U8 *)Align_down((U32)dest.u8ptr, AVR32_FLASHC_PAGE_SIZE);
             tmp.u64ptr < (U64 *)Align_down((U32)dest.u8ptr, sizeof(U64));
             tmp.u64ptr++)
          *tmp.u64ptr = *tmp.u64ptr;
      }
 
      // If destination is not 64-bit aligned...
      if (!Test_align((U32)dest.u8ptr, sizeof(U64)))
      {
        // If page erase is requested...
        if (erase)
        {
          // Fill the beginning of the flash double-word buffer with the current flash page data.
          for (i = 0; i < Get_align((U32)dest.u8ptr, sizeof(U64)); i++)
            flash_dword.u8[i] = *tmp.u8ptr++;
        }
        // If page erase is not requested...
        else
        {
          // Erase the beginning of the flash double-word buffer.
          for (i = 0; i < Get_align((U32)dest.u8ptr, sizeof(U64)); i++)
            flash_dword.u8[i] = 0xFF;
        }
 
        // Fill the end of the flash double-word buffer with the source data.
        for (; i < sizeof(U64); i++)
          flash_dword.u8[i] = *source.u8ptr++;
 
        // Align the destination pointer with its 64-bit boundary.
        dest.u64ptr = (U64 *)Align_down((U32)dest.u8ptr, sizeof(U64));
 
        // 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.
          *dest.u64ptr++ = flash_dword.u64;
        }
        // If the current destination double-word is the last one, the flash
        // double-word buffer must be kept for later.
        else flash_dword_pending = TRUE;
      }
    }
 
    // Read the source data with the maximal possible alignment and write it to
    // the page buffer with 64-bit alignment.
    switch (Get_align((U32)source.u8ptr, sizeof(U32)))
    {
    case 0:
      for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
        *dest.u64ptr++ = *source.u64ptr++;
      break;
 
    case sizeof(U16):
      for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
      {
        for (j = 0; j < sizeof(U64) / sizeof(U16); j++) flash_dword.u16[j] = *source.u16ptr++;
        *dest.u64ptr++ = flash_dword.u64;
      }
      break;
 
    default:
      for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
      {
        for (j = 0; j < sizeof(U64); j++) flash_dword.u8[j] = *source.u8ptr++;
        *dest.u64ptr++ = flash_dword.u64;
      }
    }
 
    // If the current destination page has an incomplete end...
    if (incomplete_flash_page_end)
    {
      // If the flash double-word buffer is in use, do not initialize it.
      if (flash_dword_pending) i = Get_align((U32)dest_end.u8ptr, sizeof(U64));
      // If the flash double-word buffer is free...
      else
      {
        // Fill the beginning of the flash double-word buffer with the source data.
        for (i = 0; i < Get_align((U32)dest_end.u8ptr, sizeof(U64)); i++)
          flash_dword.u8[i] = *source.u8ptr++;
      }
 
      // If page erase is requested...
      if (erase)
      {
        tmp.u8ptr = (volatile U8 *)dest_end.u8ptr;
 
        // If end of destination is not 64-bit aligned...
        if (!Test_align((U32)dest_end.u8ptr, sizeof(U64)))
        {
          // Fill the end of the flash double-word buffer with the current flash page data.
          for (; i < sizeof(U64); i++)
            flash_dword.u8[i] = *tmp.u8ptr++;
 
          // Write the flash double-word buffer to the page buffer.
          *dest.u64ptr++ = flash_dword.u64;
        }
 
        // Fill the end of the page buffer with the current flash page data.
        for (; !Test_align((U32)tmp.u64ptr, AVR32_FLASHC_PAGE_SIZE); tmp.u64ptr++)
          *tmp.u64ptr = *tmp.u64ptr;
      }
      // If page erase is not requested but end of destination is not 64-bit aligned...
      else if (!Test_align((U32)dest_end.u8ptr, sizeof(U64)))
      {
        // Erase the end of the flash double-word buffer.
        for (; i < sizeof(U64); i++)
          flash_dword.u8[i] = 0xFF;
 
        // Write the flash double-word buffer to the page buffer.
        *dest.u64ptr++ = flash_dword.u64;
      }
    }
 
    // If the current flash page is in the flash array...
    if (dest.u8ptr <= AVR32_FLASHC_USER_PAGE)
    {
      // Erase the current page if requested and write it from the page buffer.
      if (erase)
      {
        flashc_erase_page(-1, FALSE);
        error_status |= flashc_error_status;
      }
      flashc_write_page(-1);
      error_status |= flashc_error_status;
 
      // If the end of the flash array is reached, go to the User page.
      if (dest.u8ptr >= flash_array_end.u8ptr)
      {
        source.u8ptr += AVR32_FLASHC_USER_PAGE - dest.u8ptr;
        dest.u8ptr = AVR32_FLASHC_USER_PAGE;
      }
    }
    // If the current flash page is the User page...
    else
    {
      // Erase the User page if requested and write it from the page buffer.
      if (erase)
      {
        flashc_erase_user_page(FALSE);
        error_status |= flashc_error_status;
      }
      flashc_write_user_page();
      error_status |= flashc_error_status;
    }
  }
 
  // Update the FLASHC error status.
  flashc_error_status = error_status;
 
  // Return the initial destination pointer as the standard memcpy function does.
  return dst;
}
 
 
//! @}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.