/*                                              -*- mode:C++; fill-column:100 -*-
  SFBEeprom.h - Support for I/O with the EEPROM
  Copyright (C) 2009 The Regents of the University of New Mexico.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  USA
*/

/**
  \file SFBEeprom.h Reading and writing the SFB onboard EEPROM

  The SFB contains a 16KB EEPROM for storing "persistent" data.  An EEPROM (Electrically-Eraseable
  Programmable Read-Only Memory) is a device that retains data for long periods, even when powered
  off, while also allowing the stored data to be altered and updated under program control, without
  any additional hardware.

  Out of the 16,384 bytes in the SFB EEPROM, the last 128 (#EEPROM_RESERVED_BYTES) bytes are
  reserved for SFB library use, while the first 99.2% of the EEPROM is available for sketch use.

  Those final 128 EEPROM bytes are called the "SFB bootblock" or the "EEPROM reserved partition",
  while the initial 16,256 bytes are called the "EEPROM data partition".  Within the EEPROM data
  partition, there is no predefined structure or "file system", there are just persistent bytes,
  located at EEPROM addresses from 0 through 16255, available to be read and written however the
  sketch wishes.

  \par Hardware and implementation details:

  - The EEPROM is arranged in "pages" each of which is 64 (#EEPROM_BYTES_PER_PAGE) bytes long.  

  - In general, reading from the EEPROM is a relatively fast operation, taking a small number of
    microseconds per byte read, but there is also "command overhead" time, so fewer, larger reads
    will perform better than more, smaller ones:

    \code
      const int N = 1000;
      char buf[N];
      for (u32 i = 0; i < N; ++i) eepromRead(i, &buf[i], 1);  // Read first N bytes of EEPROM (slowly)
      eepromRead(0,buf,N);                                    // Has the same result, but much faster
    \endcode

  - Compared to EEPROM reading, writing to the EEPROM is slow, on the order of perhaps 5
    milliseconds.  That time is required \b for \b each \b page of EEPROM written to, regardless of
    how many bytes per page are affected:

    \code
      const int N = 50;
      char buf[N] = ..some data..
      eepromWrite(100,buf,N);   // Write 50 bytes to addrs 100..149 -- hits two pages, so TWO write cycles
      eepromWrite(128,buf,N);   // Write 50 bytes to addrs 128..177 -- hits one page, so ONE write cycle
    \endcode

  - Note, however, that #eepromWrite() returns as soon as the (final) write cycle has started,
    without waiting for it to complete, so if a single #eepromWrite() call is made that affects only
    a single page of EEPROM, it will return relatively quickly.  However, the write cycle must still
    complete before other EEPROM operations can commence:

    \code
      char buf[3];

      u32 start = micros();               // Get start time, then 
      eepromWrite(0,"foo",3);             // Write 3 bytes into one page

      u32 split = micros();               // Get split time
      eepromRead(64,buf,3);               // Read 3 bytes from one page

      u32 end = micros();                 // Get end time

      pprintf("Write=%d, read=%d\n", split-start, end-split); // Shows read slower than write!
    \endcode

    because the #eepromRead() has to wait for the write cycle started by the #eepromWrite() to
    complete.  By comparison:

    \code
      char buf[3];

      u32 start = micros();               // Get start time, then 
      eepromWrite(0,"foo",3);             // Write 3 bytes into one page
      eepromWait();                       // Wait for write cycle to complete

      u32 split = micros();               // Get split time
      eepromRead(64,buf,3);               // Read 3 bytes from one page

      u32 end = micros();                 // Get end time

      pprintf("Write=%d, read=%d\n", split-start, end-split); // Now read much faster than write!
    \endcode

  \author David H. Ackley.  
  \date (C) 2009 All rights reserved.
  \lgpl
 */
#ifndef SFBEEPROM_H_
#define SFBEEPROM_H_

#include "SFBTypes.h"

/**
 * Write data from memory to the EEPROM data partition.  Writes \a length bytes from \a source (\c
 * source[0]..source[length-1]) to EEPROM locations starting at \a addr (\c addr..addr+length-1).
 *
 * \param addr EEPROM data partition address to begin writing to.
 * \param source Memory address to begin reading from.
 * \param length Number of bytes to write.
 *
 * \blinks E_API_NULL_POINTER if source is null.
 * \blinks E_API_EEPROM_ADDRESS if addr+length is not less than eepromSize()
 *
 * \attention WARNING!  This routine returns \e before the EEPROM write cycle has completed.  If
 * that is a problem -- e.g. because the board is about to power down -- then #eepromWait() should
 * be called after #eepromWrite().
 *
 * \sideeffect EERPROM memory is written if \a length is non-zero.
 */
extern void eepromWrite(u32 addr, const u8 * source, u32 length) ;

/**
 * Read data from the EEPROM data partition into RAM.  Wait for any in-progress EEPROM write to
 * complete, if necessary, then read \a length bytes from EEPROM data partition locations starting
 * at \a addr (\c addr..addr+length-1) into RAM addresses starting at \a dest (\c
 * dest[0]..dest[length-1]).
 *
 * \param addr EEPROM data partition address to begin reading from.
 * \param dest RAM address to begin writing to.
 * \param length Number of bytes to read.
 *
 * \blinks E_API_NULL_POINTER if \a dest is null.
 * \blinks E_API_EEPROM_ADDRESS if \a addr + \a length is not less than #eepromSize()
 *
 * \sideeffect RAM is written if \a length is non-zero.
 */
extern void eepromRead(u32 addr, u8 * dest, u32 length) ;

/**
 * Wait for any in-progress EEPROM write to complete.  Normally doesn't need to be called
 * explicitly, since the other EEPROM routines wait as needed.
 *
 * \param busyWait Specify how to wait.  If \a bustWait is \c false or omitted, use #delay(u32)
 *                 function to perform the waiting, otherwise use #delayMicroseconds(u32).
 *
 * \blinks E_API_EEPROM_RESPONSE if the EEPROM fails to respond in a timely manner (i.e., four times
 * its datasheet-specified maximum latency).
 */
extern void eepromWait(bool busyWait = false) ;

/**
 * Get the EEPROM data partition size.
 *
 * \return The size of the EEPROM data partition
 */
extern u32 eepromSize() ;

#define EEPROM_SIZE_BYTES (1<<14)    /**< Total raw EEPROM size */
#define EEPROM_BYTES_PER_PAGE 64     /**< Bytes per page in the EEPROM */
#define EEPROM_RESERVED_PAGES 2      /**< Pages reserved at end of EEPROM for SFB bootblock */
#define EEPROM_RESERVED_BYTES (EEPROM_RESERVED_PAGES*EEPROM_BYTES_PER_PAGE) /**< Bytes reserved at end of EEPROM for SFB bootblock */

/**
 * Write data from memory to the EEPROM SFB reserved partition.  <b>Intended for library internal
 * use only!  Improper use can corrupt an SFB's bootblock.</b>
 *
 * Writes \a length bytes from \a data (\c data[0]..data[length-1]) to EEPROM reserved
 * locations starting at \a addr (\c addr..addr+length-1).
 *
 * \param addr EEPROM reserved partition address to begin writing to.
 * \param data Memory address to begin reading from.
 * \param length Number of bytes to write.
 *
 * \blinks E_API_NULL_POINTER if \a data is null.
 * \blinks E_API_EEPROM_ADDRESS if \a addr + \a length is greater than #EEPROM_RESERVED_BYTES
 *
 * \attention WARNING!  This routine returns \e before the EEPROM write cycle has completed.  If
 * that is a problem -- e.g. because the board is about to power down -- then #eepromWait() should
 * be called after #eepromWriteReserved() returns.
 *
 * \sideeffect EERPROM memory is written if \a length is non-zero.
 */
extern void eepromWriteReserved(const u32 addr, const u8 * data, const u32 length) ;

/**
 * Read data from the EEPROM SFB reserved partition into RAM.  <b>Intended for library internal
 * use.</b> Wait for any in-progress EEPROM write to complete, if necessary, then read \a length
 * bytes from EEPROM SFB reserved partition locations starting at \a addr (\c addr..addr+length-1)
 * into RAM addresses starting at \a data (\c data[0]..data[length-1]).
 *
 * \param addr EEPROM data partition address to begin reading from.
 * \param data RAM address to begin writing to.
 * \param length Number of bytes to read.
 *
 * \blinks E_API_NULL_POINTER if \a dest is null.
 * \blinks E_API_EEPROM_ADDRESS if \a addr + \a length is not less than #eepromSize()
 *
 * \sideeffect RAM is written if \a length is non-zero.
 */
extern void eepromReadReserved(const u32 addr, u8 * data,const u32 length) ;

/**
 * Initialize the EEPROM subsystem.  <b>Intended for library internal
 * use only.  Called automatically during SFB system startup.</b>
 *
 * \sideeffect The EEPROM subsystem is initialized.
 */
extern void eeprom_startup_initialization() ;


#endif /*SFBEEPROM_H_*/
