From af8d0ebe8f2a0bfe959ef094ef41fd9380e11916 Mon Sep 17 00:00:00 2001 From: scipio Date: Tue, 10 Apr 2007 01:21:22 +0000 Subject: [PATCH] Fixed timer system to follow new implementation from DGay. --- .../atm128/timer/sim/HplAtm128CompareC.nc | 6 +- .../atm128/timer/sim/HplAtm128Timer0AsyncC.nc | 23 +- .../atm128/timer/sim/HplAtm128Timer0AsyncP.nc | 576 ++++++++++++++++++ 3 files changed, 585 insertions(+), 20 deletions(-) create mode 100644 tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncP.nc diff --git a/tos/chips/atm128/timer/sim/HplAtm128CompareC.nc b/tos/chips/atm128/timer/sim/HplAtm128CompareC.nc index cae3c539..ae40ffca 100644 --- a/tos/chips/atm128/timer/sim/HplAtm128CompareC.nc +++ b/tos/chips/atm128/timer/sim/HplAtm128CompareC.nc @@ -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 { diff --git a/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncC.nc b/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncC.nc index dc32120e..4cc1f607 100644 --- a/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncC.nc +++ b/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncC.nc @@ -42,28 +42,17 @@ configuration HplAtm128Timer0AsyncC { provides { - interface Init @atleastonce(); // 8-bit Timers interface HplAtm128Timer as Timer; interface HplAtm128TimerCtrl8 as TimerCtrl; interface HplAtm128Compare 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 index 00000000..c07c1dd0 --- /dev/null +++ b/tos/chips/atm128/timer/sim/HplAtm128Timer0AsyncP.nc @@ -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 + * @author Martin Turon + * @author David Gay + */ + +// $Id$/// $Id: HplAtm128Timer2C.nc, + +#include +#include + +module HplAtm128Timer0AsyncP { + provides { + interface HplAtm128Timer as Timer0; + interface HplAtm128TimerCtrl8 as Timer0Ctrl; + interface HplAtm128Compare 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 lastZero + * 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; + } + } +} -- 2.39.2