code: /**
   * Copyright (c) 2006, Regents of the University of California
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * Redistributions of source code must retain the above copyright notice,
   * this list of conditions and the following disclaimer.
   *
   * 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.
   *
   * Neither the name of the University of California, Los Angeles nor the
   * names of its contributors may be used to endorse or promote products
   * derived from this software without specific prior written permission.
   *
   * 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
   * OWNER 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.
   */
  
  package avrora.sim.mcu;
  
  import avrora.sim.*;
  import avrora.sim.clock.Clock;
  import cck.util.Util;
  import java.util.*;
  
  /**
   * The <code>ATMegaTimer</code> class implements a timer on the ATMega series
   * of microcontrollers.
   *
   * @author Pekka Nikander
   */
  public abstract class ATMegaTimer extends AtmelInternalDevice {
  
      /*
      * The current mode of this timer.  The mode determines how to
      * increment/decrement the counter, when to load new register
      * values, when to signal TOPs/BOTTOMs/overflows, etc.
      *
      * Note that the mode may change as the WGMn bits are set.
      */ Mode mode;
  
      /*
       * A collection of Input, Output, and InputOutputCompareUnits
       * implemented by this timer.
       */
      final Map<String,Comparator> comparators = new HashMap<String, Comparator>();
  
      boolean timerEnabled;
      boolean countUp;
      long period;
  
      /* pg. 93 of manual. Block compareMatch for one period after
       * TCNTn is written to. */ boolean compareMatchBlocked;
  
      protected final Clock externalClock;
      Clock timerClock;
      int timerNumber;
      /*
       * Fields shared by all ATMega timers.
       */
      final RegisterSet.Field TOIEn; // Timer Overflow Interrupt Enable
      final FlagField TOVn;  // Timer Overflow flag
      final RegisterSet.Field WGMn;  // Mode
      final RegisterSet.Field CSn;   // Clock source
  
      /*
       * Timer periods, as assigned by the CSn field
       */
      private final int[] periods;
  
      protected ATMegaTimer(int n, AtmelMicrocontroller m, int[] p, String ovfName) {
          super("Timer" + n, m);
          timerNumber = n;
          periods = p;
  
          RegisterSet rset = m.getRegisterSet();
  
          TOIEn = rset.getField("TOIE" + n);
          int overflowInterrupt = m.properties.getInterrupt(ovfName);
          TOVn = new FlagField(interpreter.getInterruptTable(), true, overflowInterrupt);
          rset.installField("TOV" + n, TOVn);
          WGMn = rset.installField("WGM" + n, newWGMField());
          CSn = rset.installField("CS" + n, newPeriodField());
  
  
          externalClock = m.getClock("external");
          timerClock = mainClock;
      }
  
      private RegisterSet.Field newPeriodField() {
          return new RegisterSet.Field() {
              public void update() {
                  resetPeriod(periods[value]);
              }
          };
      }
  
      private RegisterSet.Field newWGMField() {
          return new RegisterSet.Field() {
              public void update() {
                  resetMode(value);
              }
          };
      }
  
      protected void addComparator(String name, Comparator comparator) {
          comparators.put(name, comparator);
      }
  
      protected Comparator getComparator(String name) {
          return (Comparator)comparators.get(name);
      }
  
      /**
       * Resets the clock period.  Called whenever CSn is assigned.
       */
      private void resetPeriod(int nPeriod) {
          if (nPeriod == 0) {
              if (timerEnabled) {
                  if (devicePrinter != null) devicePrinter.println(name + " disabled");
                  timerClock.removeEvent(mode);
                  timerEnabled = false;
              }
              return;
          }
          if (timerEnabled) {
              timerClock.removeEvent(mode);
          }
          if (devicePrinter != null)
              devicePrinter.println(name + " enabled: period = " + nPeriod + " mode = " + WGMn.value);
          period = nPeriod;
          timerEnabled = true;
          timerClock.insertEvent(mode, period);
  
      }
  
      /**
       * Resets the mode according to the WGMn bits.
       */
      public abstract void resetMode(int WGMn);
  
      /**
       * Returns current counter value
       */
      public abstract int getCounter();
  
      /**
       * Sets the current counter value
       */
      public abstract void setCounter(int count);
  
      /*
       * Returns the counter register name.
       */
      public abstract String getCounterName();
  
      /**
       * Returns the size-specific MAX value.
       */
      protected abstract int getMax();
  
      /**
       * Sets the overflow flag (TOVn) for this timer.
       */
      protected void signalOverflow() {
          if (devicePrinter != null) {
              devicePrinter.println(name + ".overFlow (interrupts enabled: " + TOIEn.value + ')');
          }
          TOVn.flag();
      }
  
      /**
       * The <code>TopValue</code> interface allows the mode-specific TOP value to be abstracted.  Depending on
       * the mode, TOP may be either defined by a register or by a constant.
       */
      interface TopValue {
  
          public int mask();
  
          public int read16();
  
          public void flush();
      }
  
      /**
       * The <code>FlagField</code> implements a <code>Field</code> that works as an interrupt posted flag.
       * <p/>
       * XXX: This is a wrong place for this class, since it is generic and not Timer specific.  Please refactor
       * it to elsewhere.  However, I didn't want to create a dependency between RegisterSets and
       * InterruptTables, and therefore didn't initially implement this in RegisterSet.
       */
      public class FlagField extends RegisterSet.Field implements InterruptTable.Notification {
  
          InterruptTable interrupts;
          int inum;
          boolean autoclear;
  
          public FlagField(InterruptTable it, boolean auto, int in) {
              interrupts = it;
              autoclear = auto;
              inum = in;
              interrupts.registerInternalNotification(this, inum);
          }
  
          public void update() {
              if (0 != value) {
                  interrupts.post(inum);
              } else {
                  interrupts.unpost(inum);
              }
          }
  
          public void flag() {
              write(1);
          }
  
          public void unflag() {
              write(0);
          }
  
          public void force(int inum) {
              // XXX: Should assert that this.inum == inum?
              flag();
          }
  
          public void invoke(int inum) {
              // XXX: Should assert that this.inum == inum?
              if (autoclear) {
                  unflag();
              }
          }
      }
  
      /**
       * The <code>Mode</code> implements the mode dependent, periodic behavior of the timer. It emulates the
       * operation of the timer at each clock cycle and uses the global timed event queue to achieve the correct
       * periodic behavior.
       * <p/>
       * The Mode abstract the specifics of the timer mode into a simpler interface, allowing the commonality
       * between the different ATMega timers to be implemented by a single, fairly simple set of methods.
       */
      protected /*final*/ class Mode implements Simulator.Event {
  
          /**
           * A mode-dependint TOP value, either a constant or a register, typically COMnA or COMnI.
           */
          final TopValue top;
  
          /**
           * A mode-dependent FLAG bit, set when TOP is hit. May be <code>null</code>.
           */
          final FlagField flag;
  
          /**
           * A mode-specific algorithm object, specifying the details of counting.
           */
          final Strategy strategy;
  
          /**
           * Creates a new, mutable Mode object.
           */
          protected Mode(Class<? extends Strategy> sc, RegisterSet.Field f, ActiveRegister t) {
              this(sc, f, (TopValue)t);
          }
  
          /**
           * Creates a new, mutable Mode object.
           */
          protected Mode(Class<? extends Strategy> sc, RegisterSet.Field f, TopValue t) {
              if (NORMAL.class == sc) {
                  strategy = new NORMAL();
              } else if (CTC.class == sc) {
                  strategy = new CTC();
              } else if (PWM.class == sc) {
                  strategy = new PWM();
              } else if (FC_PWM.class == sc) {
                  strategy = new FC_PWM();
              } else if (FAST_PWM.class == sc) {
                  strategy = new FAST_PWM();
              } else {
                  throw new Error("Unknown Strategy class " + sc);
              }
              flag = (FlagField)f;
              top = t;
          }
  
          /**
           * Returns the mode-specific TOP value: fixed, OCRnA, OCRnI, ...
           */
          protected int getTop() {
              return top.read16();
          }
  
          /**
           * Signals hitting the top; depending on the mode, sets the corresponding signal bit.
           */
          protected void signalTop() {
              if (null != flag) flag.flag();
          }
  
          /**
           * Updates the TOP register; called either at TOP or BOTTOM, depending on the mode
           */
          protected void updateTop() {
              top.flush();
          }
  
          /**
           * Called by the appropriate clock whenever the strategy should tick.
           */
          public void fire() {
              int value = getCounter();
              if (devicePrinter != null) {
                  devicePrinter.println(name + " [" + getCounterName() + " = " + value);
                  for (Comparator c : comparators.values()) {
                      devicePrinter.println(", " + c + "(actual) = " + c.read() + ", " + c + "(buffer) = " + c.readBuffer() + ']');
                  }
              }
  
              value = strategy.nextValue(value);
  
              // the compare match should be performed in any case.
              // XXX: Check that this is OK; is using count ok
              if (!compareMatchBlocked) {
                  for (Comparator i : comparators.values()) {
                      i.compare(value);
                  }
              }
  
              setCounter(value);
  
              // XXX: verify the timing on this.
              compareMatchBlocked = false;
  
              if (period != 0) timerClock.insertEvent(this, period);
          }
  
          protected void registerWritten(BufferedRegister reg) {
              strategy.registerWritten(reg);
          }
  
          protected abstract class Strategy {
  
              protected abstract int nextValue(int count);
  
              protected abstract void registerWritten(BufferedRegister reg);
          }
  
          protected class NORMAL extends Strategy {
  
              protected int nextValue(int count) {
                  count++;
                  if (getMax() + 1 == count) {
                      signalOverflow();
                      count = 0;
                  }
                  return count;
              }
  
              protected void registerWritten(BufferedRegister reg) {
                  // Flush the buffer immediately
                  reg.flush();
              }
          }
  
          protected class CTC extends Strategy {
  
              protected int nextValue(int count) {
                  count++;
  
                  if (getTop() == count) {
                      signalTop();
                      count = 0;
                  }
                  if (getMax() + 1 == count) {
                      signalOverflow();
                      count = 0;
                  }
                  return count;
              }
  
              protected void registerWritten(BufferedRegister reg) {
                  // Flush the buffer immediately
                  reg.flush();
              }
          }
  
          protected class FAST_PWM extends Strategy {
  
              boolean zero = false;
  
              protected int nextValue(int count) {
                  count++;
  
                  if (zero) {
                      zero = false;
                      count = 0;
                      updateTop();
                      signalOverflow();
                  }
                  if (getTop() == count) {
                      zero = true; // Zero counter on next clock cycle
                      signalTop();
                  }
                  return count;
              }
  
              protected void registerWritten(BufferedRegister reg) {
                  // Mask in fixed FASTPWM modes
                  reg.value &= top.mask();
                  // Flushing is delayed
              }
          }
  
          protected class PWM extends Strategy {
  
              protected int nextValue(int count) {
                  if (countUp) count++;
                  else count--;
  
                  if (count == getTop()) {
                      countUp = false;
                      signalTop();
                      updateTop();
                  }
                  if (count == 0) {
                      countUp = true;
                      signalOverflow();
                  }
                  return count;
              }
  
              protected void registerWritten(BufferedRegister reg) {
                  // Mask in fixed FASTPWM modes
                  reg.value &= top.mask();
                  // Flushing is delayed until top
              }
          }
  
          protected class FC_PWM extends Strategy {
  
              protected int nextValue(int count) {
                  if (countUp) count++;
                  else count--;
  
                  if (count == getTop()) {
                      countUp = false;
                      signalTop();
                  }
                  if (count == 0) {
                      countUp = true;
                      signalOverflow();
                      updateTop();
                  }
                  return count;
              }
  
              protected void registerWritten(BufferedRegister reg) {
                  // Flushing is delayed until bottom
              }
          }
      }
  
      /**
       * In some CTC and PWM modes, the TOP values is a fixed one.
       */
      static class FixedTop implements TopValue {
  
          public static final FixedTop FF = new FixedTop(0xFF);
          public static final FixedTop _1FF = new FixedTop(0x1FF);
          public static final FixedTop _3FF = new FixedTop(0x3FF);
          public static final FixedTop FFFF = new FixedTop(0xFFFF);
  
          final int top;
  
          protected FixedTop(int t) {
              top = t;
          }
  
          public int read16() {
              return top;
          }
  
          public int mask() {
              return top;
          }
  
          public void flush() {
              throw Util.failure("Fixed top value flushed");
          }
      }
  
      /**
       * Abstract base class for InputCompareUnits and OutputCompareUnits.
       * <p/>
       * Implements the common functionality between 8- and 16-bit, Input and Output units.
       */
      abstract class Comparator {
  
          public static final String _ = "";
          public static final String A = "A";
          public static final String B = "B";
          public static final String C = "C";
          public static final String I = "I";
          final String type;
          final String unit;
          final AtmelMicrocontroller.Pin pin;
          final FlagField flag;
  
          Comparator(String t, String u, RegisterSet rset, int interruptNumber, AtmelMicrocontroller.Pin p) {
              type = t;
              unit = u;
              pin = p;
              InterruptTable it = interpreter.getInterruptTable();
              flag = new FlagField(it, true, interruptNumber);
              rset.installField(type + "F" + timerNumber + unit, flag);
          }
  
          public String toString() {
              return type + "R" + timerNumber + unit;
          }
  
          void compare(int count) {
              if (read() == count) {
                  operate();
                  flag.flag();    // Should be next clock cycle
              }
          }
  
          protected abstract void operate();
  
          abstract int read();
  
          abstract int readBuffer();
      }
  
      abstract class OutputComparator extends Comparator {
  
          final RegisterSet.Field pinmode;
          final RegisterSet.Field force;
  
          OutputComparator(String u, RegisterSet rset, int interruptNumber, AtmelMicrocontroller.Pin p) {
              super("OC", u, rset, interruptNumber, p);
              pinmode = rset.getField("COM" + timerNumber + unit);
              force = rset.installField("FOC" + timerNumber + unit, new RegisterSet.Field() {
                  public void update() {
                      if (1 == value) {
                          operate();
                      }
                  }
              });
          }
  
          protected void operate() {
              if (null == pin) return;
  
              // read the bits in the control register for compare mode
              // XXX: This should be more mode specific, see pg 133-134 of the
              // ATMega 128 manual
              switch (pinmode.value) {
                  case 1:
                      pin.write(!pin.read()); // clear
                      break;
                  case 2:
                      pin.write(false);
                      break;
                  case 3:
                      pin.write(true);
                      break;
              }
          }
  
      }
  
      abstract class InputComparator extends Comparator {
  
          InputComparator(String u, RegisterSet rset, int interruptNumber, AtmelMicrocontroller.Pin p) {
              super("IC", u, rset, interruptNumber, p);
          }
  
          protected void operate() {
              // XXX: Should capture here
          }
      }
  
      /**
       * Overloads the write behavior of this class of register in order to implement compare match blocking for
       * one timer period.
       * <p/>
       * XXX: Make this into a facade
       */
      protected class TCNTnRegister implements ActiveRegister {
  
          public final String name;
          private final ActiveRegister register;
  
          protected TCNTnRegister(String n, ActiveRegister r) {
              name = n;
              register = r;
          }
  
          public void write(byte val) {
              register.write(val);
              compareMatchBlocked = true;
          }
  
          public byte read() {
              return register.read();
          }
  
      }
  
      /**
       * In PWM modes, writes to the OCRnx registers may be buffered, depending on the mode. Specifically, the
       * actual write may be delayed until a certain event (the counter reaching either TOP or BOTTOM) specified
       * by the particular PWN mode. BufferedRegister implements this by writing to a buffer register on a write
       * and reading from the buffered register in a read. When the buffered register is to be updated, the
       * <code>flush()</code> method should be called.
       * <p/>
       * Note that the underlying register may be either an 8-bit or 16-bit register; the BufferedRegister is
       * oblivious to that.
       */
      protected class BufferedRegister extends RW16Register implements TopValue, ActiveRegister {
  
          /*
           * XXX: The implementation of this class is currently very ugly,
           * as it provides interfaces for both 16 and 8 bit registers.
           *
           * It attempts to implement the Decorator pattern allowing any
           * existing register to be buffered.  However, currently the
           * underlying framework keeps 8-bit and 16-bit registers separate,
           * making the implementation of this class challenging.
           *
           * Notes:
           * - RW16Register is inherited only because there are lacking
           *   interface inheritance.  It's implementation is not used for anyting.
           * - It would be better if read16() didn't exist but it was
           *   unified with read().
           */
          int value;              // The buffered value, 8 or 16 bits
  
          private final ActiveRegister reg8; // The underlying register, 8-bit
          private final RW16Register reg16; // The underlying register, 16-bit
  
          protected BufferedRegister(ActiveRegister r) {
              this.reg8 = r;
              this.reg16 = null;
          }
  
          protected BufferedRegister(RW16Register r) {
              this.reg16 = r;
              this.reg8 = null;
          }
  
          public void write(byte val) {
              value = val;
              mode.registerWritten(this);
          }
  
          public void write(int val) {
              value = val;
              mode.registerWritten(this);
          }
  
          public int readBuffer() {
              return super.read16();
          }
  
          public byte read() {
              return (byte)read16();
          }
  
          public int read16() {
              return (null != reg8) ? reg8.read() : reg16.read16();
          }
  
          public int mask() {
              return 0xffff;    // When used as TOP, never mask any bits, independent of the size
          }
  
          public void flush() {
              if (null != reg8) reg8.write((byte)value);
              else reg16.write(value);
          }
      }
  
      /**
       * A temporary register shared by all 16-bit registers in a 16-bit timer/counter. See
       * <code>LowRegister</code> for more information. While this is not needed in 8-bit timers, it doesn't pay
       * much to implement it here.
       */
      protected final RWRegister tempHighReg = new RWRegister();
  
      /**
       * The <code>LowRegister</code> and <code>HIghRegister</code> classes exists to implement the shared
       * temporary register for the high byte of the 16-bit registers corresponding to a 16-bit timer. Accesses
       * to the high byte of a register pair should go through this temporary byte. According to the manual,
       * writes to the high byte are stored in the temporary register. When the low byte is written to, both the
       * low and high byte are updated. On a read, the temporary high byte is updated when a read occurs on the
       * low byte. The LowRegister should be installed in place of the low register. Reads/writes on this
       * register will act accordingly on the low register, as well as initiate a read/write on the associated
       * high register.
       */
      protected class LowRegister implements ActiveRegister {
  
          final RW16Register reg;
  
          LowRegister(RW16Register r) {
              reg = r;
          }
  
          public void write(byte val) {
              reg.write((tempHighReg.read() << 8) + val);
          }
  
          public byte read() {
              tempHighReg.write((byte)(reg.read16() >> 8));
              return (byte)reg.read16();
          }
  
      }
  
      /**
       * For other 16-bit registers but OCRnxH, both reads and writes go through the temporary register.
       */
      protected class HighRegister implements ActiveRegister {
  
          public void write(byte val) {
              tempHighReg.write(val);
          }
  
          public byte read() {
              return tempHighReg.read();
          }
  
      }
  
      /**
       * Writes to OCRnxH go through the temporary register
       * but writes are direct.
       */
      protected class OCRnxHighRegister extends HighRegister {
  
          final RW16Register reg;
  
          OCRnxHighRegister(RW16Register r) {
              reg = r;
          }
  
          public byte read() {
              return (byte)(reg.read16() >> 8);
          }
  
      }
  
  }
  
  