⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 flash_amd29lv065d.c

📁 周立功SOPC 嵌入式系统实验教程书籍配套光盘 FLASH驱动代码
💻 C
字号:
/*
  file: flash_AM29LV065d.c

  contents: implementation of flash routines
            for the AMD part.

        author: Aaron Ferrucci

  10/21/02  Support for 8bit data on
        Nios Pro flash AM29LV065d.
        Tested by writing to AM29LV065d
        on Nios Pro flash (only 8bit data).

  11/8/04  Marcus Negron
  	Changed arbitrary AMD algorithm addresses (am29lv065d don't cares)
  	to specific algorithm addresses (other CFI command 02 flash cares)
  	so that this driver may work with other devices than the am29lv065d,
  	like perhaps am29lv128m or Atmel flash, or just "look like each other".
	Also, fixed the sector map to be correct for am29lv065d.
	Test using NDKs 1S10, 1S40, and 1C20 (8bit data).
*/


#include "excalibur.h"


// +-----------------------------
// | Some CPU's need to have their
// | caches flushed all the time when
// | dealing with flash. The Nios 2
// | definitely does. It does not hurt
// | to do this for first generation
// | Nios, but it is not needed and
// | makes the code just barely too
// | large for the Germs monitor.
// |
// | So, here, we squelch it.
// |

#if defined(nm_cpu_architecture) && (nm_cpu_architecture == nios2)
#define DO_CACHE_INIT 1
#else
#define DO_CACHE_INIT 0
#endif


// ---------------------------------------------
// Private Routines

// Wait for the given address to contain 0xFF.
// Return value: 1 on success, 0 on timeout failure.

static int await_erase_complete(int entire_chip, volatile unsigned char *addr)
{
  int iTimeout = 40;

  // The "typical" time for an erase command is 0.7 seconds.
  // Empirically (as expected), it takes longer to erase the entire
  // chip than a single sector. Give a generous timeout for a single
  // sector, and even more for the entire chip.

  if (entire_chip)
    iTimeout = 20000;

  while (iTimeout)
  {
    nr_delay(100);      // Wait 100 ms.
    if (0xFF == *addr)  // done? note: 8bit data
                  {
                    return 0;     // leave
                  }

    iTimeout--;
  }

  // Timeout error.
  return -1;
}

// This routine actually takes about 3 us on a 33.333MHz Nios32.
static wait_at_least_1_us(void)
{
  volatile unsigned long iTimeout = nasys_clock_freq_1000 / 8250;
  while (iTimeout--)
  {
    ;
  }
}

// Unlock bypass mode, enabling fast writes.
static void unlock_bypass_mode(volatile unsigned char *flash_base)
{ // 065d
  flash_base[0xAAA] = 0xAA; // unlock bypass command - cycle 1
  flash_base[0x555] = 0x55; // unlock bypass command - cycle 2
  flash_base[0xAAA] = 0x20; // unlock bypass command - cycle 3
}

// Turn bypass mode off, disabling fast writes and enabling normal function.
static void reset_bypass_mode(volatile unsigned char *flash_base)
{
  *flash_base = 0x90; // exit unlock bypass reset command - cycle 1
  *flash_base = 0x0;  // exit unlock bypass reset command - cycle 2
}

// Read the given address until two successive reads yield the
// same value.
// Return value: 0 on success, -1 on timeout failure.
static int await_write_complete(volatile unsigned char *addr, const unsigned char correct_value)
{
        //
        // TPA 2/14/2003: The *Maximum* programming time is 150us.
        //                Waiting several times the maximum time
        //                seems like a much better idea than giving-up
        //                well before the published spec says we
        //                should.
        //
  unsigned long iTimeout = 600;
  int return_value = -1; // Pessimistic return value.

  while (iTimeout)
  {
    wait_at_least_1_us();

    // While the flash is working on program data, read accesses return
    // "status" instead of data.  Status bit 7 is the complement of the
    // data being written.  When the program operation is complete and
    // successful, the written data is returned.  So, read the written
    // address until it equals the data written.

    if (*addr == correct_value)
      break;
    iTimeout--;
  }

  if (iTimeout)
    return_value = 0;

  return return_value;
}

static int erase_flash_single_command(volatile unsigned char *flash_base)
{
  int result;

#if DO_CACHE_INIT
  nr_dcache_init();
  nr_icache_init();
#endif

// Erase the entire flash in one command. AKA CHIP ERASE on AM29LV065d
  flash_base[0xAAA] = 0xAA; // 1st cycle
  flash_base[0x555] = 0x55; // 2nd cycle
  flash_base[0xAAA] = 0x80; // 3rd cycle
  flash_base[0xAAA] = 0xAA; // 4th cycle
  flash_base[0x555] = 0x55; // 5th cycle
  flash_base[0xAAA] = 0x10; // 6th cycle

  result = await_erase_complete(1, flash_base);
}               // (entire chip, base)


// Write val to the given flash address, in bypass mode (assumes
// that bypass mode has been enabled already).
// Return value: 0 on success, -1 on failure.
static int nr_flash_write_bypass(volatile unsigned char *flash_base,
  volatile unsigned char *addr, unsigned char val)
{
  unsigned char us1, us2;
  int iTimeout;
  int result = 0;

  nm_dcache_invalidate_line(addr);
  nm_icache_invalidate_line(addr);

  *flash_base = 0xA0;   // unlock bypass program command - 1st cycle
  *addr = val;          // program address and data    - 2nd cycle

  result = await_write_complete(addr,val);
  if(result)
    return result;

  us1 = *addr;

  if (us1 != val)
    result = -1;

  return result;
}

// ---------------------------------------------
// Public Routines

// Erase the flash sector at sector_address.
// Return value: 0 on success, -1 on failure.

int nr_flash_erase_sector
  (
      unsigned short *flash_base,
      unsigned short *sector_address
  )
{
  volatile unsigned char *fb = (unsigned char *) flash_base;
  volatile unsigned char *sa = (unsigned char *) sector_address;
  int result;

#if DO_CACHE_INIT
  nr_dcache_init();
  nr_icache_init();
#endif

#ifdef nasys_main_flash
  if (-1 == (int)fb)
    fb = nasys_main_flash;
#endif // nasys_main_flash
//  AM29LV065d
  fb[0xAAA] = 0xAA; // 1st cycle
  fb[0x555] = 0x55; // 2nd cycle
  fb[0xAAA] = 0x80; // 3rd cycle
  fb[0xAAA] = 0xAA; // 4th cycle
  fb[0x555] = 0x55; // 5th cycle

  *sa = 0x30; // 6th cycle

  // Loop until the data reads as 0xFF.

  result = await_erase_complete(0, sa);
  return result;
}


// Erase the entire flash.
// Return value: 0 on success, -1 on failure.

int nr_flash_erase(unsigned short *flash_base)
{
  volatile unsigned char *fb = (unsigned char*) flash_base;
  int result = 0;

#if DO_CACHE_INIT
  nr_dcache_init();
  nr_icache_init();
#endif

#if __nios32__
#define ALLSECTORERASE 1
#if ALLSECTORERASE
#ifdef nasys_main_flash
  if (-1 == (int)fb)
  {
    fb = nasys_main_flash;
  }
#endif // nasys_main_flash

  result = erase_flash_single_command(fb);
  return result;

#else // !ALLSECTORERASE

  int i;

  // For nios32, the AM29LV065d is divided into sectors as follows:
  // [0x000000, 0x010000)
  // [0x010000, 0x020000)
  // [0x020000, 0x030000)
  // etc...
  int sectorOffset[] =  // AM29LV065d sector map
  {
    0x000000, 0x010000, 0x020000, 0x030000,
    0x040000, 0x050000, 0x060000, 0x070000,
    0x080000, 0x090000, 0x0A0000, 0x0B0000,
    0x0C0000, 0x0D0000, 0x0E0000, 0x0F0000,
    0x100000, 0x110000, 0x120000, 0x130000,
    0x140000, 0x150000, 0x160000, 0x170000,
    0x180000, 0x190000, 0x1A0000, 0x1B0000,
    0x1C0000, 0x1D0000, 0x1E0000, 0x1F0000,
    0x200000, 0x210000, 0x220000, 0x230000,
    0x240000, 0x250000, 0x260000, 0x270000,
    0x280000, 0x290000, 0x2A0000, 0x2B0000,
    0x2C0000, 0x2D0000, 0x2E0000, 0x2F0000,
    0x300000, 0x310000, 0x320000, 0x330000,
    0x340000, 0x350000, 0x360000, 0x370000,
    0x380000, 0x390000, 0x3A0000, 0x3B0000,
    0x3C0000, 0x3D0000, 0x3E0000, 0x3F0000,
    0x400000, 0x410000, 0x420000, 0x430000,
    0x440000, 0x450000, 0x460000, 0x470000,
    0x480000, 0x490000, 0x4A0000, 0x4B0000,
    0x4C0000, 0x4D0000, 0x4E0000, 0x4F0000,
    0x500000, 0x510000, 0x520000, 0x530000,
    0x540000, 0x550000, 0x560000, 0x570000,
    0x580000, 0x590000, 0x5A0000, 0x5B0000,
    0x5C0000, 0x5D0000, 0x5E0000, 0x5F0000,
    0x600000, 0x610000, 0x620000, 0x630000,
    0x640000, 0x650000, 0x660000, 0x670000,
    0x680000, 0x690000, 0x6A0000, 0x6B0000,
    0x6C0000, 0x6D0000, 0x6E0000, 0x6F0000,
    0x700000, 0x710000, 0x720000, 0x730000,
    0x740000, 0x750000, 0x760000, 0x770000,
    0x780000, 0x790000, 0x7A0000, 0x7B0000,
    0x7C0000, 0x7D0000, 0x7E0000, 0x7F0000,
  };

  if (-1 == (int)fb)
    fb = nasys_main_flash;

  // Erase each sector in turn.

  for (i = 0; i < sizeof(sectorOffset) / sizeof(*sectorOffset); ++i)
  {
    int sector = (int)fb + sectorOffset[i];

    result = nr_flash_erase_sector(fb, (unsigned char*)sector);
    if(result)
      break;
  }
  return result;

#endif // ALLSECTORERASE
#else // !__nios32__
#ifdef nasys_main_flash
  if (-1 == (int)fb)
    fb = nasys_main_flash;
#endif // nasys_main_flash

  result =  erase_flash_single_command(fb);
  return result;

#endif // __nios32__
}

// Write val to the given flash address.
// Return value: 1 on success, 0 on failure.
int amd29lv065d_flash_write_byte
  (
    unsigned char *flash_base,
    unsigned char *addr,
    unsigned char val
  )
{
  volatile unsigned char *fb = flash_base;
  volatile unsigned char *a = addr;
  unsigned char us1, us2;
  int result = 0;

  nm_dcache_invalidate_line(a);
  nm_icache_invalidate_line(a);

#ifdef nasys_main_flash
  if (-1 == (int)fb)
    fb = nasys_main_flash;
#endif // nasys_main_flash

  fb[0x555] = 0xAA; // 1st cycle  addr = XXX, data = AA
  fb[0x333] = 0x55; // 2nd cycle  addr = XXX, data = 55
  fb[0x555] = 0xA0; // 3rd cycle  addr = XXX, data = A0

  *a = val;     // 4th cycle  addr = PA, data = PD

  result = await_write_complete(a,val);
  if(result)
    return result;

  us1 = *a;
  if (us1 != val)
    result = -1;

  return result;
}

int nr_flash_write
    (
    unsigned short *flash_base,
    unsigned short *addr,
    unsigned short val
    )
{
  unsigned char* fb       = (unsigned char *) flash_base;
  unsigned char* a        = (unsigned char *) addr;
  unsigned char  byte_val = val & 0xff;
  int result;

  result = amd29lv065d_flash_write_byte (fb, a,   byte_val);

  // Nonzero result means error.
  if (result)
    return result;

  byte_val = (val >> 8) & 0xff;
  result = amd29lv065d_flash_write_byte (fb, a+1, byte_val);
  return result;
}

// Write a buffer of data to the flash, using bypass mode.
// Return value: 1 on success, 0 on failure.
// Note: the integer "size" is given as a number of half-words to
// write.   How convenient.  We write this 8-bit-wide flash one byte
// at a time (of course).
int nr_flash_write_buffer
    (
    unsigned short *flash_base,
    unsigned short *start_address,
    unsigned short *buffer,
    int size
    )
{
  volatile unsigned char *fb  = (unsigned char *) flash_base;
                 unsigned char *sa  = (unsigned char *) start_address;
                 unsigned char *buf = (unsigned char *) buffer;
        int num_bytes = size * 2;
  int i;
  int result = 0;

#ifdef nasys_main_flash
  if (-1 == (int)fb)
    fb = nasys_main_flash;
#endif // nasys_main_flash

  unlock_bypass_mode(fb);
  for (i = 0; i < num_bytes; ++i)
  {
    result = nr_flash_write_bypass(fb, sa + i, buf[i]);
    if(result)
      break;

  }
  reset_bypass_mode(fb);

  return result;
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -