/*                                              -*- mode:C++; fill-column:100 -*-
  SFBProcessor.h CPU speed setting and control routines for the SFB.
  Copyright (C) 2010 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

  $Id$
*/

/**
  \file SFBProcessor.h CPU speed setting and control routines via the #Processor object.
  \author David H. Ackley.  
  \date (C) 2010 All rights reserved.
  \lgpl

  \since 0.9.14
 */

#ifndef SFBPROCESSOR_H
#define SFBPROCESSOR_H

#include "SFBTypes.h"     /* For u8 */
#include "clock.h"        /* For CSC_12MHZ, etc */

/**
   Wrapper class implementing the #Processor object for access to CPU speed control.

  \since 0.9.14
 */
class SFBProcessor {
public:

  /** Change the CPU clock to its fastest-computing (most power-consuming) available value.  See
      also #setMinSpeed.  See notes at #setCode for possible implications of speed changes.
  */
  void setMaxSpeed() { setCode(CSC_MAX_MHZ); }

  /** Change the CPU clock to its most power-conserving (slowest-computing) available value. See
      also #setMaxSpeed.  See notes at #setCode for possible implications of speed changes.
  */
  void setMinSpeed() { setCode(CSC_MIN_MHZ); }

  /** Return the slowest supported CPU clock speed, in millions of cycles-per-second.  Currently the
      slowest possible speed is 12MHz.
  */
  u32 getMHzMin() { return 12*getCodeMin(); }

  /** Return the slowest supported CPU clock speed, as a 'CPU Speed Code'.  Currently the slowest
      possible speed code is #CSC_12MHZ.
  */
  u32 getCodeMin() { return CSC_MIN_MHZ; }

  /** Return the fastest supported CPU clock speed, in millions of cycles-per-second.  Currently the
      fastest possible speed is 72MHz.
  */
  u32 getMHzMax() { return 12*getCodeMax(); }

  /** Return the fastest supported CPU clock speed, as a 'CPU Speed Code'.  Currently the fastest
      possible speed code is #CSC_72MHZ.
  */
  u32 getCodeMax() { return CSC_MAX_MHZ; }

  /** Return the current CPU clock speed in cycles-per-second.
      \returns From 12,000,000 to 72,000,000 by 12,000,000's
  */
  u32 getHz() { return getMHz()*1000000; }

  /** Return the current CPU clock speed in millions of cycles-per-second.
      \returns From 12 to 72 by 12's
  */
  u32 getMHz() { return getCode()*12; }

  /** Return the current CPU clock speed as a 'CPU Speed Code'.
      \returns From 1 (#CSC_12MHZ) to 6 (#CSC_72MHZ) by 1's
  */
  u32 getCode() ;

  /** Change the clock speed to \a hz cycles-per-second, or to the next lower available value, if \a
      hz is not an exactly supported value.  Higher speeds compute faster; lower speeds save power.
      See notes at #setCode for implications of speed changes.

      \blinks #E_API_ARGUMENT if \a hz is less than 12,000,000 or greater than 72,000,000.
  */

  void setHz(u32 hz) { setMHz(hz/1000000); }

  /** Change the clock speed to \a mhz million cycles-per-second, or to the next lower available
      value, if \a mhz is not an exactly supported value.  Higher speeds compute faster; lower
      speeds save power.  See notes at #setCode for implications of speed changes.

      \blinks #E_API_ARGUMENT if \a mhz is less than 12 or greater than 72.
  */

  void setMHz(u32 mhz) { setCode(mhz/12); }

  /** Change the clock speed to that specified by \a code.  Higher speed codes (e.g., #CSC_72MHZ)
      compute faster; lower speed codes (e.g., #CSC_12MHZ) save power.

      Although time-based calls such as #millis(), #micros(), and #delay() are essentially \e
      unaffected by processor speed changes, one must be sensitive to the obvious and inescapable
      fact that at slower speeds computing stuff \b just \b takes \b longer.  If you explore with
      the #Profile object, for example, at slower speeds you'll see interrupt overhead, as well as
      alarm and packet handlers, eating more and more time.

      And beyond that, sketch programmers must also \e be \e aware of these important less obvious
      \b limitations and \b warnings about changing the CPU speed:
      -# It takes some time -- possibly tens of milliseconds -- to change the CPU speed.  
      -# The maximum baud rate that can be performed reliably declines with the clock speed.  While
         the overall sustainable data rate always depends on particulars of the given sketch, the
         maximum baud rate that will be negotiated by the core software -- normally #SFBAUD500000 --
         declines to #SFBAUD230400 at #CSC_24MHZ, and to #SFBAUD115200 at #CSC_12MHZ.
      -# Peripheral hardware operations \b may be disrupted during the speed change.  For example,
         inbound and/or outbound serial data may be lost or corrupted, and hardware timers may
         glitch.  Sketch code must be robust to such events when speed changing is contemplated.
      -# After a speed change, the hardware peripherals that are managed by the core software ---
         including TIMER0, the voltage monitoring ADCs, and any UARTs that have been started via
         SFBSerial::begin() or SFBSerial::begin(u32 baud, bool manual) --- are automatically
         adjusted.  \b But if a sketch has done any additional hardware configuration, it is the
         \b sketch's \b responsibility to compensate however necessary after a speed change.  See
         #getCurrentClockConfiguration() and #setClockSpeedChangeCallback().

      \blinks #E_API_ARGUMENT if \a code is less that #CSC_MIN_MHZ or greater than #CSC_MAX_MHZ.
  */
  void setCode(u32 code);

};

typedef void (*SpeedChangeHandler)();
struct SpeedChangeHandlerBlock {
  SpeedChangeHandler handler;
  SpeedChangeHandlerBlock * next;
};

extern void registerSpeedChangeHandlerBlock(SpeedChangeHandlerBlock *); // Defined in startup.cpp!

/**
   The premade instance of the #SFBProcessor class, used for accessing and controlling the onboard
   CPU speed (and thus power consumption).
 */
extern SFBProcessor Processor; 

#endif /* SFBPROCESSOR_H */
