]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
Fixed timer system to follow new implementation from DGay.
authorscipio <scipio>
Tue, 10 Apr 2007 01:21:22 +0000 (01:21 +0000)
committerscipio <scipio>
Tue, 10 Apr 2007 01:21:22 +0000 (01:21 +0000)
tos/chips/atm128/timer/sim/HplAtm128CompareC.nc
tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncC.nc
tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncP.nc [new file with mode: 0644]

index cae3c5392e45d5e79100ff03883edad5be9e7c37..ae40ffca55c5064c3f8a19d284d2b1192541e65d 100644 (file)
@@ -192,13 +192,13 @@ implementation {
       return;
     }
     else {
-      char time[128];
-      sim_print_now(time, 128);
+      char timeStr[128];
+      sim_print_now(timeStr, 128);
       dbg("HplAtm128CompareC", "Handling compare at 0x%p @ %s\n", evt, sim_time_string());
            
       if (READ_BIT(interruptRegister, interruptBit)) {
        CLR_BIT(flagRegister, flagBit);
-       dbg("HplAtm128CompareC", "Compare interrupt @ %s\n", time);
+       dbg("HplAtm128CompareC", "Compare interrupt @ %s\n", timeStr);
        SIG_OUTPUT_COMPARE0();
       }
       else {
index dc32120e1ea49d93652c360a8b2d518ed84e2a7c..4cc1f607cdf96d1c0bc32a4575fa6cd7d496ded8 100644 (file)
 configuration HplAtm128Timer0AsyncC
 {
   provides {
-    interface Init @atleastonce();
     // 8-bit Timers
     interface HplAtm128Timer<uint8_t>   as Timer;
     interface HplAtm128TimerCtrl8       as TimerCtrl;
     interface HplAtm128Compare<uint8_t> as Compare;
+    interface HplAtm128TimerAsync       as TimerAsync;
   }
 }
 implementation {
-  components HplAtm128Counter0C, new HplAtm128CompareC(uint8_t,
-                                                ATM128_OCR0,
-                                                ATM128_TIMSK,
-                                                OCIE0,
-                                                ATM128_TIFR,
-                                                OCF0);
-
-  Init = HplAtm128Counter0C;
-  Timer = HplAtm128Counter0C;
-  TimerCtrl = HplAtm128Counter0C;
-  Compare = HplAtm128CompareC;
-
-  HplAtm128CompareC.Timer -> HplAtm128Counter0C;
-  HplAtm128CompareC.TimerCtrl -> HplAtm128Counter0C;
-  HplAtm128CompareC.Notify -> HplAtm128Counter0C;
-  
+  components HplAtm128Timer0AsyncP;
+  Timer = HplAtm128Timer0AsyncP;
+  TimerCtrl = HplAtm128Timer0AsyncP;
+  Compare = HplAtm128Timer0AsyncP;
+  TimerAsync = HplAtm128Timer0AsyncP;
 }
diff --git a/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncP.nc b/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncP.nc
new file mode 100644 (file)
index 0000000..c07c1dd
--- /dev/null
@@ -0,0 +1,576 @@
+/*
+ * "Copyright (c) 2005 Stanford University. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose, without fee, and without written
+ * agreement is hereby granted, provided that the above copyright
+ * notice, the following two paragraphs and the author appear in all
+ * copies of this software.
+ * 
+ * IN NO EVENT SHALL STANFORD UNIVERSITY BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF STANFORD UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * 
+ * STANFORD UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE
+ * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND STANFORD UNIVERSITY
+ * HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
+ * ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+/**
+ * The TOSSIM implementation of the Atm128 Timer0 counter. It handles
+ * overflow, scaling, and phase considerations.
+ *
+ * @date November 22 2005
+ *
+ * @author Philip Levis <pal@cs.stanford.edu>
+ * @author Martin Turon <mturon@xbow.com>
+ * @author David Gay <dgay@intel-research.net>
+ */
+
+// $Id$/// $Id: HplAtm128Timer2C.nc,
+
+#include <Atm128Timer.h>
+#include <hardware.h>
+
+module HplAtm128Timer0AsyncP {
+  provides {
+    interface HplAtm128Timer<uint8_t>   as Timer0;
+    interface HplAtm128TimerCtrl8       as Timer0Ctrl;
+    interface HplAtm128Compare<uint8_t> as Compare;
+    interface HplAtm128TimerAsync       as TimerAsync;
+  }
+}
+implementation {
+  bool inOverflow = 0;
+  uint8_t savedCounter = 0;
+
+  void adjust_zero(uint8_t currentCounter);
+
+  void cancel_overflow();
+  sim_event_t* allocate_overflow();
+  void configure_overflow(sim_event_t* e);
+  void schedule_new_overflow();
+
+  sim_time_t clock_to_sim(sim_time_t t);
+  sim_time_t sim_to_clock(sim_time_t t);
+  uint16_t shiftFromScale();
+
+  /* lastZero keeps track of the phase of the clock. It denotes the sim
+   * time at which the underlying clock started, which is needed to
+   * calculate when compares will occur. */
+  sim_time_t lastZero = 0;
+
+  /** This variable is needed to keep track of when the underlying
+   *  timer starts, in order to reset lastZero. When oldScale is
+   *  AVR_CLOCK_OFF and the scale is set to something else, the
+   *  clock starts ticking. */
+  uint8_t oldScale = AVR_CLOCK_OFF;
+  
+  void adjust_zero(uint8_t currentCounter);
+  
+  void cancel_compare();
+  sim_event_t* allocate_compare();
+  void configure_compare(sim_event_t* e);
+  void schedule_new_compare();
+
+  sim_time_t clock_to_sim(sim_time_t t);
+  sim_time_t sim_to_clock(sim_time_t t);
+  uint16_t shiftFromScale();
+
+  default async event void Compare.fired() { }
+  AVR_ATOMIC_HANDLER(SIG_OUTPUT_COMPARE0) {
+    //stabiliseTimer0();
+    signal Compare.fired();
+  }
+  default async event void Timer0.overflow() { }
+  AVR_ATOMIC_HANDLER(SIG_OVERFLOW0) {
+    inOverflow = TRUE;
+    signal Timer0.overflow();
+    inOverflow = FALSE;
+  }
+
+
+  
+  sim_time_t last_zero() {
+    if (lastZero == 0) {
+      lastZero = sim_mote_start_time(sim_node());
+    }
+    return lastZero;
+  }
+
+
+  void notify_changed() {
+    uint8_t newScale = call Timer0.getScale();
+    if (newScale != AVR_CLOCK_OFF &&
+       oldScale == AVR_CLOCK_OFF) {
+      lastZero = sim_time();
+    }
+    oldScale = newScale;
+    
+    schedule_new_compare();
+  }
+
+  sim_time_t notify_clockTicksPerSec() {
+    return ATM128_TIMER0_TICKSPPS;
+  }
+  
+  /**
+   * If the clock was stopped and has restarted, then
+   * we need to move the time when the clock was last
+   * zero to a time that reflects the current settings.
+   * For example, if the clock was stopped when the counter
+   * was 52 and then later restarted, then <tt>lastZero</tt>
+   * needs to be moved forward in time so that the 52
+   * reflects the current time.
+   */ 
+  void adjust_zero(uint8_t currentCounter) {
+    sim_time_t now = sim_time();
+    sim_time_t adjust = currentCounter;
+    adjust = adjust << shiftFromScale();
+    adjust = clock_to_sim(adjust);
+    lastZero = now - adjust;
+  }
+  
+  sim_time_t clock_to_sim(sim_time_t t) {
+    t *= sim_ticks_per_sec();
+    t /= notify_clockTicksPerSec();
+    return t;
+  }
+
+  sim_time_t sim_to_clock(sim_time_t t) {
+    t *= notify_clockTicksPerSec();
+    t /= sim_ticks_per_sec();
+    return t;
+  }
+  
+  uint16_t shiftFromScale() {
+    uint8_t scale = call Timer0.getScale();
+    switch (scale) {
+    case 0:
+      return 0;
+    case 1:
+      return 0;
+    case 2:
+      return 3;
+    case 3:
+      return 5;
+    case 4:
+      return 6;
+    case 5:
+      return 7;
+    case 6:
+      return 8;
+    case 7:
+      return 10;
+    default:
+      return 255;
+    }
+    
+  }
+
+  sim_event_t* compare;
+
+  void timer0_compare_handle(sim_event_t* evt) {
+    dbg("HplAtm128Timer0AsyncP", "Beginning compare 0x%p at %s\n", evt, sim_time_string());
+    if (evt->cancelled) {
+      return;
+    }
+    else {
+      char timeStr[128];
+      sim_print_now(timeStr, 128);
+      dbg("HplAtm128Timer0AsyncP", "Handling compare at 0x%p @ %s\n", evt, sim_time_string());
+      
+      if (READ_BIT(ATM128_TCCR0, WGM01) && !READ_BIT(ATM128_TCCR0, WGM00)) {
+       dbg("HplAtm128Timer0AsyncP", "%s: CTC is set, clear timer.\n", __FUNCTION__);
+       call Timer0.set(0);
+      }
+      else {
+       dbg("HplAtm128Timer0AsyncP", "%s: TCCR is 0x%hhx, %i, %i\n", __FUNCTION__, TCCR0, (int)READ_BIT(ATM128_TCCR0, WGM01), (int)READ_BIT(ATM128_TCCR0, WGM00));
+      }
+      
+      if (READ_BIT(ATM128_TIMSK, OCIE0)) {
+       dbg("HplAtm128Timer0AsyncP", "TIFR is %hhx\n", TIFR);
+       CLR_BIT(ATM128_TIFR, OCF0);
+       dbg("HplAtm128Timer0AsyncP", "TIFR is %hhx\n", TIFR);
+       dbg("HplAtm128Timer0AsyncP", "Compare interrupt @ %s\n", timeStr);
+       SIG_OUTPUT_COMPARE0();
+      }
+      else {
+       SET_BIT(ATM128_TIFR, OCF0);
+      }
+      // If we haven't been cancelled
+      if (!evt->cancelled) {
+       configure_compare(evt);
+       sim_queue_insert(evt);
+      }
+    }
+  }
+
+  sim_event_t* allocate_compare() {
+    sim_event_t* newEvent = sim_queue_allocate_event();
+    dbg("HplAtm128Timer0AsyncP", "Allocated compare at 0x%p\n", newEvent);
+    newEvent->handle = timer0_compare_handle;
+    newEvent->cleanup = sim_queue_cleanup_none;
+    return newEvent;
+  }
+  
+  void configure_compare(sim_event_t* evt) {
+    sim_time_t compareTime = 0;
+    sim_time_t phaseOffset = 0;
+    uint8_t timerVal = call Timer0.get();
+    uint8_t compareVal = call Compare.get();
+
+    // Calculate how many counter increments until timer
+    // hits compare, considering wraparound, and special
+    // case of complete wraparound.
+    compareTime = ((compareVal - timerVal) & 0xff);
+    if (compareTime == 0) {
+      compareTime = 256;
+    }
+
+    // Now convert the compare time from counter increments
+    // to simulation ticks, considering the fact that the
+    // increment actually has a phase offset.
+    // The +1 is from the timer behavior: if you set OCR0 to be X,
+    // it will actually fire when TCNT is X+1
+    compareTime = (compareTime + 1) << shiftFromScale();
+    compareTime = clock_to_sim(compareTime);
+    compareTime += sim_time();
+
+    // How long into a timer tick was the clock actually reset?
+    // This covers the case when the compare is set midway between
+    // a tick, so it will go off a little early
+    phaseOffset = sim_time();
+    phaseOffset -= last_zero();
+    phaseOffset %= clock_to_sim(1 << shiftFromScale());
+    compareTime -= phaseOffset;
+      
+    dbg("HplAtm128Timer0AsyncP", "Configuring new compare of %i for %i at time %llu  (@ %llu)\n", (int)compareVal, sim_node(), compareTime, sim_time());
+    
+    evt->time = compareTime;    
+  }
+  
+  void schedule_new_compare() {
+    if (compare != NULL) {
+      cancel_compare();
+    }
+    if (call Timer0.getScale() != AVR_CLOCK_OFF) {
+      sim_event_t* newEvent = allocate_compare();
+      configure_compare(newEvent);
+
+      compare = newEvent;
+      sim_queue_insert(newEvent);
+    }
+  }
+
+  
+  //=== Read the current timer value. ===================================
+  async command uint8_t  Timer0.get() {
+    uint8_t rval;
+    sim_time_t elapsed = sim_time() - last_zero();
+    elapsed = sim_to_clock(elapsed);
+    elapsed = elapsed >> shiftFromScale();
+    rval = (uint8_t)(elapsed & 0xff);
+    dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Getting timer: %hhu\n", rval);
+    return rval;
+  }
+
+  //=== Set/clear the current timer value. ==============================
+  /**
+   * Set/clear the current timer value.
+   *
+   * This code is pretty tricky.  */
+  async command void Timer0.set(uint8_t newVal)  {
+    uint8_t curVal = call Timer0.get();
+    dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Setting timer: %hhu\n", newVal);
+    if (newVal == curVal) {
+      return;
+    }
+    else {
+      sim_time_t adjustment = curVal - newVal;
+      adjustment = adjustment << shiftFromScale();
+      adjustment = clock_to_sim(adjustment);
+
+      if (newVal < curVal) {
+       lastZero += adjustment;
+      }
+      else { // newVal > curVal
+       lastZero -= adjustment;
+      }
+
+      schedule_new_overflow();
+      notify_changed();
+    }
+  }
+
+  //=== Read the current timer scale. ===================================
+  async command uint8_t Timer0.getScale() {
+    return TCCR0 & 0x7;
+  }
+
+  //=== Turn off the timers. ============================================
+  async command void Timer0.off() {
+    call Timer0.setScale(AVR_CLOCK_OFF);
+    savedCounter = call Timer0.get();
+    cancel_overflow();
+    notify_changed();
+  }
+
+  //=== Write a new timer scale. ========================================
+  async command void Timer0.setScale(uint8_t s)  {
+    Atm128TimerControl_t ctrl;
+    uint8_t currentScale = call Timer0.getScale();
+    uint8_t currentCounter;
+    dbg("HplAtm128Timer0AsyncP", "Timer0 scale set to %i\n", (int)s);
+    if (currentScale == 0) {
+      currentCounter = savedCounter;
+    }
+    else {
+      currentCounter = call Timer0.get();
+    }
+    
+    ctrl = call Timer0Ctrl.getControl();
+    ctrl.flat &= ~(0x7);
+    ctrl.flat |= (s & 0x7);
+    call Timer0Ctrl.setControl(ctrl);  
+
+    if (currentScale != s) {
+      adjust_zero(currentCounter);
+      schedule_new_overflow();
+    }
+    notify_changed();
+  }
+
+  //=== Read the control registers. =====================================
+  async command Atm128TimerControl_t Timer0Ctrl.getControl() { 
+    return *(Atm128TimerControl_t*)&TCCR0; 
+  }
+
+  //=== Write the control registers. ====================================
+  async command void Timer0Ctrl.setControl( Atm128TimerControl_t x ) {
+    dbg("HplAtm128Timer0AsyncP", "Setting control to be 0x%hhx\n", x.flat);
+    TCCR0 = x.flat; 
+  }
+
+  //=== Read the interrupt mask. =====================================
+  async command Atm128_TIMSK_t Timer0Ctrl.getInterruptMask() { 
+    return *(Atm128_TIMSK_t*)&TIMSK; 
+  }
+
+  //=== Write the interrupt mask. ====================================
+  DEFINE_UNION_CAST(TimerMask8_2int, Atm128_TIMSK_t, uint8_t);
+  DEFINE_UNION_CAST(TimerMask16_2int, Atm128_ETIMSK_t, uint8_t);
+
+  async command void Timer0Ctrl.setInterruptMask( Atm128_TIMSK_t x ) { 
+    TIMSK = TimerMask8_2int(x); 
+  }
+
+  //=== Read the interrupt flags. =====================================
+  async command Atm128_TIFR_t Timer0Ctrl.getInterruptFlag() {
+    Atm128_TIFR_t at;
+    at.flat = TIFR;
+    return at;
+  }
+
+  //=== Write the interrupt flags. ====================================
+  DEFINE_UNION_CAST(TimerFlags8_2int, Atm128_TIFR_t, uint8_t);
+  DEFINE_UNION_CAST(TimerFlags16_2int, Atm128_ETIFR_t, uint8_t);
+
+  async command void Timer0Ctrl.setInterruptFlag( Atm128_TIFR_t x ) { 
+    TIFR = TimerFlags8_2int(x); 
+  }
+
+  //=== Timer 8-bit implementation. ====================================
+  async command void Timer0.reset() {
+    // Clear TOV0. On real hardware, this is a write.
+    TIFR &= ~(1 << TOV0);
+  }
+  async command void Timer0.start() {
+    SET_BIT(ATM128_TIMSK, TOIE0);
+    dbg("HplAtm128Timer0AsyncP", "Enabling TOIE0 at %llu\n", sim_time());
+    schedule_new_overflow();
+  }
+  async command void Timer0.stop()  {
+    dbg("HplAtm128Timer0AsyncP", "Timer stopped @ %llu\n", sim_time());
+    CLR_BIT(ATM128_TIMSK, TOIE0);
+    cancel_overflow();
+  }
+
+  bool overflowed() {
+    return READ_BIT(ATM128_TIFR, TOV0); 
+  }
+
+  inline void stabiliseOverflow() {
+    /* From the atmel manual:
+
+    During asynchronous operation, the synchronization of the interrupt
+    flags for the asynchronous timer takes three processor cycles plus one
+    timer cycle.  The timer is therefore advanced by at least one before
+    the processor can read the timer value causing the setting of the
+    interrupt flag. The output compare pin is changed on the timer clock
+    and is not synchronized to the processor clock.
+
+    So: if the timer is = 0, wait till it's = 1, except if
+    - we're currently in the overflow interrupt handler
+    - or, the overflow flag is already set
+    */
+
+    //if (!inOverflow)
+    //  while (!TCNT0 && !overflowed())
+    //;
+  }
+  
+  async command bool Timer0.test()  { 
+    stabiliseOverflow();
+    return overflowed();
+  }
+
+  async command bool Timer0.isOn()  { 
+    return (call Timer0Ctrl.getInterruptMask()).bits.toie0; 
+  }
+
+  async command void Compare.reset() { TIFR = 1 << OCF0; }
+  async command void Compare.start() { SET_BIT(ATM128_TIMSK,OCIE0); }
+  async command void Compare.stop()  { CLR_BIT(ATM128_TIMSK,OCIE0); }
+  async command bool Compare.test()  { 
+    return (call Timer0Ctrl.getInterruptFlag()).bits.ocf0; 
+  }
+  async command bool Compare.isOn()  { 
+    return (call Timer0Ctrl.getInterruptMask()).bits.ocie0; 
+  }
+
+  //=== Read the compare registers. =====================================
+  async command uint8_t Compare.get()   {
+    dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Getting compare: %hhu\n", OCR0);
+    return OCR0;
+  }
+  
+  //=== Write the compare registers. ====================================
+  async command void Compare.set(uint8_t t)   {
+    dbg("HplAtm128Timer0AsyncP", "HplAtm128Timer0AsyncP: Setting compare: %hhu\n", t);
+    atomic {
+       /* Re the comment above: it's a bad idea to wake up at time 0, as
+          we'll just spin when setting the next deadline. Try and reduce
+          the likelihood by delaying the interrupt...
+       */
+      if (t == 0 || t >= 0xfe)
+       t = 1;
+      
+      if (t != OCR0) {
+       OCR0 = t;
+       schedule_new_compare();
+      }
+    }
+  }
+
+  sim_event_t* overflow;  
+  void timer0_overflow_handle(sim_event_t* evt) {
+    if (evt->cancelled) {
+      return;
+    }
+    else {
+      if (READ_BIT(ATM128_TIMSK, TOIE0)) {
+       CLR_BIT(ATM128_TIFR, TOV0);
+       dbg("HplAtm128Timer0AsyncP", "Overflow interrupt at %s\n", sim_time_string());
+       SIG_OVERFLOW0();
+      }
+      else {
+       dbg("HplAtm128Timer0AsyncP", "Setting overflow bit at %s\n", sim_time_string());
+       SET_BIT(ATM128_TIFR, TOV0);
+      }
+      configure_overflow(evt);
+      sim_queue_insert(evt);
+    }
+  }
+  
+  sim_event_t* allocate_overflow() {
+    sim_event_t* newEvent = sim_queue_allocate_event();
+
+    newEvent->handle = timer0_overflow_handle;
+    newEvent->cleanup = sim_queue_cleanup_none;
+    return newEvent;
+  }
+  
+  void configure_overflow(sim_event_t* evt) {
+    sim_time_t overflowTime = 0;
+    uint8_t timerVal = call Timer0.get();
+    uint8_t overflowVal = 0;
+
+    // Calculate how many counter increments until timer
+    // hits compare, considering wraparound, and special
+    // case of complete wraparound.
+    overflowTime = ((overflowVal - timerVal) & 0xff);
+    if (overflowTime == 0) {
+      overflowTime = 256;
+    }
+
+    // Now convert the compare time from counter increments
+    // to simulation ticks, considering the fact that the
+    // increment actually has a phase offset.
+    overflowTime = overflowTime << shiftFromScale();
+    overflowTime = clock_to_sim(overflowTime);
+    overflowTime += sim_time();
+    overflowTime -= (sim_time() - last_zero()) % (1 << shiftFromScale());
+
+    dbg("HplAtm128Timer0AsyncP", "Scheduling new overflow for %i at time %llu\n", sim_node(), overflowTime);
+    
+    evt->time = overflowTime;
+  }
+  
+  void schedule_new_overflow() {
+    sim_event_t* newEvent = allocate_overflow();
+    configure_overflow(newEvent);
+
+    if (overflow != NULL) {
+      cancel_overflow();
+    }
+    overflow = newEvent;
+    sim_queue_insert(newEvent);
+  }
+  
+  void cancel_overflow() {
+    if (overflow != NULL) {
+      overflow->cancelled = 1;
+      dbg("HplAtm128Timer0AsyncP", "Cancelling overflow %p.\n", overflow);
+      overflow->cleanup = sim_queue_cleanup_total;
+    }
+  }
+
+  async command Atm128Assr_t TimerAsync.getAssr() {
+    return *(Atm128Assr_t *)&ASSR;
+  }
+
+  async command void TimerAsync.setAssr(Atm128Assr_t x) {
+    ASSR = x.flat;
+  }
+
+  async command void TimerAsync.setTimer0Asynchronous() {
+    ASSR |= 1 << AS0;
+  }
+
+  async command int TimerAsync.controlBusy() {
+    return (ASSR & (1 << TCR0UB)) != 0;
+  }
+
+  async command int TimerAsync.compareBusy() {
+    return (ASSR & (1 << OCR0UB)) != 0;
+  }
+
+  async command int TimerAsync.countBusy() {
+    return (ASSR & (1 << TCN0UB)) != 0;
+  }
+
+  void cancel_compare() {
+    dbg("HplAtm128CompareC", "Cancelling compare at 0x%p\n", compare);
+    if (compare != NULL) {
+      compare->cancelled = 1;
+      compare->cleanup = sim_queue_cleanup_total;
+    }
+  }
+}