timer.c
Go to the documentation of this file.00001 00039 #include "timer.h" 00040 #include "hw/hw_timer.h" 00041 00042 #include "cfg/cfg_timer.h" 00043 #include "cfg/cfg_wdt.h" 00044 #include "cfg/cfg_proc.h" 00045 #include "cfg/cfg_signal.h" 00046 #include <cfg/os.h> 00047 #include <cfg/debug.h> 00048 #include <cfg/module.h> 00049 00050 #include <cpu/attr.h> 00051 #include <cpu/types.h> 00052 #include <cpu/irq.h> 00053 #include <cpu/power.h> // cpu_relax() 00054 00055 #include <kern/preempt.h> // proc_decQuantun() 00056 00057 /* 00058 * Include platform-specific binding code if we're hosted. 00059 * Try the CPU specific one for bare-metal environments. 00060 */ 00061 #if OS_HOSTED 00062 //#include OS_CSOURCE(timer) 00063 #include <emul/timer_posix.c> 00064 #else 00065 #ifndef WIZ_AUTOGEN 00066 #warning Deprecated: now you should include timer_<cpu> directly in the makefile. Remove this line and the following once done. 00067 #include CPU_CSOURCE(timer) 00068 #endif 00069 #endif 00070 00071 /* 00072 * Sanity check for config parameters required by this module. 00073 */ 00074 #if !defined(CONFIG_KERN) || ((CONFIG_KERN != 0) && CONFIG_KERN != 1) 00075 #error CONFIG_KERN must be set to either 0 or 1 in config.h 00076 #endif 00077 #if !defined(CONFIG_WATCHDOG) || ((CONFIG_WATCHDOG != 0) && CONFIG_WATCHDOG != 1) 00078 #error CONFIG_WATCHDOG must be set to either 0 or 1 in config.h 00079 #endif 00080 00081 #if CONFIG_WATCHDOG 00082 #include <drv/wdt.h> 00083 #endif 00084 00085 #if defined (CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS 00086 #include <kern/signal.h> /* sig_wait(), sig_check() */ 00087 #include <kern/proc.h> /* proc_current() */ 00088 #include <cfg/macros.h> /* BV() */ 00089 #endif 00090 00091 00102 #if !defined(CONFIG_TIMER_STROBE) || !CONFIG_TIMER_STROBE 00103 #define TIMER_STROBE_ON do {/*nop*/} while(0) 00104 #define TIMER_STROBE_OFF do {/*nop*/} while(0) 00105 #define TIMER_STROBE_INIT do {/*nop*/} while(0) 00106 #endif 00107 00108 00110 volatile ticks_t _clock; 00111 00112 00113 #if CONFIG_TIMER_EVENTS 00114 00118 REGISTER static List timers_queue; 00119 00124 INLINE void timer_addToList(Timer *timer, List *queue) 00125 { 00126 /* Inserting timers twice causes mayhem. */ 00127 ASSERT(timer->magic != TIMER_MAGIC_ACTIVE); 00128 DB(timer->magic = TIMER_MAGIC_ACTIVE;) 00129 00130 00131 /* Calculate expiration time for this timer */ 00132 timer->tick = _clock + timer->_delay; 00133 00134 /* 00135 * Search for the first node whose expiration time is 00136 * greater than the timer we want to add. 00137 */ 00138 Timer *node = (Timer *)LIST_HEAD(queue); 00139 while (node->link.succ) 00140 { 00141 /* 00142 * Stop just after the insertion point. 00143 * (this fancy compare takes care of wrap-arounds). 00144 */ 00145 if (node->tick - timer->tick > 0) 00146 break; 00147 00148 /* Go to next node */ 00149 node = (Timer *)node->link.succ; 00150 } 00151 00152 /* Enqueue timer request into the list */ 00153 INSERT_BEFORE(&timer->link, &node->link); 00154 } 00155 00163 void timer_add(Timer *timer) 00164 { 00165 ATOMIC(timer_addToList(timer, &timers_queue)); 00166 } 00167 00174 Timer *timer_abort(Timer *timer) 00175 { 00176 ATOMIC(REMOVE(&timer->link)); 00177 DB(timer->magic = TIMER_MAGIC_INACTIVE;) 00178 00179 return timer; 00180 } 00181 00182 00183 INLINE void timer_poll(List *queue) 00184 { 00185 Timer *timer; 00186 00187 /* 00188 * Check the first timer request in the list and process 00189 * it when it has expired. Repeat this check until the 00190 * first node has not yet expired. Since the list is sorted 00191 * by expiry time, all the following requests are guaranteed 00192 * to expire later. 00193 */ 00194 while ((timer = (Timer *)LIST_HEAD(queue))->link.succ) 00195 { 00196 /* This request in list has not yet expired? */ 00197 if (timer_clock() - timer->tick < 0) 00198 break; 00199 00200 /* Retreat the expired timer */ 00201 REMOVE(&timer->link); 00202 DB(timer->magic = TIMER_MAGIC_INACTIVE;) 00203 00204 /* Execute the associated event */ 00205 event_do(&timer->expire); 00206 } 00207 } 00208 00213 void synctimer_add(Timer *timer, List *queue) 00214 { 00215 timer_addToList(timer, queue); 00216 } 00217 00235 void synctimer_poll(List *queue) 00236 { 00237 timer_poll(queue); 00238 } 00239 00240 #endif /* CONFIG_TIMER_EVENTS */ 00241 00242 00248 void timer_delayTicks(ticks_t delay) 00249 { 00250 /* We shouldn't sleep with interrupts disabled */ 00251 IRQ_ASSERT_ENABLED(); 00252 00253 #if CONFIG_KERN_SIGNALS 00254 Timer t; 00255 00256 if (proc_preemptAllowed()) 00257 { 00258 ASSERT(!sig_check(SIG_SINGLE)); 00259 timer_setSignal(&t, proc_current(), SIG_SINGLE); 00260 timer_setDelay(&t, delay); 00261 timer_add(&t); 00262 sig_wait(SIG_SINGLE); 00263 } 00264 else 00265 #endif /* !CONFIG_KERN_SIGNALS */ 00266 { 00267 ticks_t start = timer_clock(); 00268 00269 /* Busy wait */ 00270 while (timer_clock() - start < delay) 00271 cpu_relax(); 00272 } 00273 } 00274 00275 00276 #if CONFIG_TIMER_UDELAY 00277 00284 void timer_busyWait(hptime_t delay) 00285 { 00286 hptime_t now, prev = timer_hw_hpread(); 00287 hptime_t delta; 00288 00289 for(;;) 00290 { 00291 now = timer_hw_hpread(); 00292 /* 00293 * We rely on hptime_t being unsigned here to 00294 * reduce the modulo to an AND in the common 00295 * case of TIMER_HW_CNT. 00296 */ 00297 delta = (now - prev) % TIMER_HW_CNT; 00298 if (delta >= delay) 00299 break; 00300 delay -= delta; 00301 prev = now; 00302 } 00303 } 00304 00312 void timer_delayHp(hptime_t delay) 00313 { 00314 if (UNLIKELY(delay > us_to_hptime(1000))) 00315 { 00316 timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC)); 00317 delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC); 00318 } 00319 00320 timer_busyWait(delay); 00321 } 00322 #endif /* CONFIG_TIMER_UDELAY */ 00323 00328 DEFINE_TIMER_ISR 00329 { 00330 /* 00331 * With the Metrowerks compiler, the only way to force the compiler generate 00332 * an interrupt service routine is to put a pragma directive within the function 00333 * body. 00334 */ 00335 #ifdef __MWERKS__ 00336 #pragma interrupt saveall 00337 #endif 00338 00339 /* 00340 * On systems sharing IRQ line and vector, this check is needed 00341 * to ensure that IRQ is generated by timer source. 00342 */ 00343 if (!timer_hw_triggered()) 00344 return; 00345 00346 TIMER_STROBE_ON; 00347 00348 /* Perform hw IRQ handling */ 00349 timer_hw_irq(); 00350 00351 /* Update the master ms counter */ 00352 ++_clock; 00353 00354 /* Update the current task's quantum (if enabled). */ 00355 proc_decQuantum(); 00356 00357 #if CONFIG_TIMER_EVENTS 00358 timer_poll(&timers_queue); 00359 #endif 00360 00361 TIMER_STROBE_OFF; 00362 } 00363 00364 MOD_DEFINE(timer) 00365 00366 00369 void timer_init(void) 00370 { 00371 #if CONFIG_KERN_IRQ 00372 MOD_CHECK(irq); 00373 #endif 00374 00375 #if CONFIG_TIMER_EVENTS 00376 LIST_INIT(&timers_queue); 00377 #endif 00378 00379 TIMER_STROBE_INIT; 00380 00381 _clock = 0; 00382 00383 timer_hw_init(); 00384 00385 MOD_INIT(timer); 00386 } 00387 00388 00389 #if (ARCH & ARCH_EMUL) 00390 00393 void timer_cleanup(void) 00394 { 00395 MOD_CLEANUP(timer); 00396 00397 timer_hw_cleanup(); 00398 00399 // Hmmm... apparently, the demo app does not cleanup properly 00400 //ASSERT(LIST_EMPTY(&timers_queue)); 00401 } 00402 #endif /* ARCH_EMUL */
