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/proc_p.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 * Search for the first node whose expiration time is 00132 * greater than the timer we want to add. 00133 */ 00134 Timer *node = (Timer *)LIST_HEAD(queue); 00135 while (node->link.succ) 00136 { 00137 /* 00138 * Stop just after the insertion point. 00139 * (this fancy compare takes care of wrap-arounds). 00140 */ 00141 if (node->tick - timer->tick > 0) 00142 break; 00143 00144 /* Go to next node */ 00145 node = (Timer *)node->link.succ; 00146 } 00147 00148 /* Enqueue timer request into the list */ 00149 INSERT_BEFORE(&timer->link, &node->link); 00150 } 00151 00159 void timer_add(Timer *timer) 00160 { 00161 ATOMIC( 00162 /* Calculate expiration time for this timer */ 00163 timer->tick = _clock + timer->_delay; 00164 00165 timer_addToList(timer, &timers_queue); 00166 ); 00167 } 00168 00175 Timer *timer_abort(Timer *timer) 00176 { 00177 ATOMIC(REMOVE(&timer->link)); 00178 DB(timer->magic = TIMER_MAGIC_INACTIVE;) 00179 00180 return timer; 00181 } 00182 00183 00184 INLINE void timer_poll(List *queue) 00185 { 00186 Timer *timer; 00187 00188 /* 00189 * Check the first timer request in the list and process 00190 * it when it has expired. Repeat this check until the 00191 * first node has not yet expired. Since the list is sorted 00192 * by expiry time, all the following requests are guaranteed 00193 * to expire later. 00194 */ 00195 while ((timer = (Timer *)LIST_HEAD(queue))->link.succ) 00196 { 00197 /* This request in list has not yet expired? */ 00198 if (timer_clock() - timer->tick < 0) 00199 break; 00200 00201 /* Retreat the expired timer */ 00202 REMOVE(&timer->link); 00203 DB(timer->magic = TIMER_MAGIC_INACTIVE;) 00204 00205 /* Execute the associated event */ 00206 event_do(&timer->expire); 00207 } 00208 } 00209 00214 void synctimer_add(Timer *timer, List *queue) 00215 { 00216 timer->tick = timer_clock() + timer->_delay; 00217 00218 timer_addToList(timer, queue); 00219 } 00220 00221 void synctimer_readd(Timer *timer, List *queue) 00222 { 00223 timer->tick += timer->_delay; 00224 timer_addToList(timer, queue); 00225 } 00226 00227 00245 void synctimer_poll(List *queue) 00246 { 00247 timer_poll(queue); 00248 } 00249 00250 #endif /* CONFIG_TIMER_EVENTS */ 00251 00252 00258 void timer_delayTicks(ticks_t delay) 00259 { 00260 /* We shouldn't sleep with interrupts disabled */ 00261 IRQ_ASSERT_ENABLED(); 00262 00263 #if CONFIG_KERN_SIGNALS 00264 Timer t; 00265 DB(t.magic = TIMER_MAGIC_INACTIVE;) 00266 if (proc_preemptAllowed()) 00267 { 00268 ASSERT(!sig_check(SIG_SINGLE)); 00269 timer_setSignal(&t, proc_current(), SIG_SINGLE); 00270 timer_setDelay(&t, delay); 00271 timer_add(&t); 00272 sig_wait(SIG_SINGLE); 00273 } 00274 else 00275 #endif /* !CONFIG_KERN_SIGNALS */ 00276 { 00277 ticks_t start = timer_clock(); 00278 00279 /* Busy wait */ 00280 while (timer_clock() - start < delay) 00281 cpu_relax(); 00282 } 00283 } 00284 00285 00286 #if CONFIG_TIMER_UDELAY 00287 00294 void timer_busyWait(hptime_t delay) 00295 { 00296 hptime_t now, prev = timer_hw_hpread(); 00297 hptime_t delta; 00298 00299 for (;;) 00300 { 00301 now = timer_hw_hpread(); 00302 /* 00303 * The timer counter may wrap here and "prev" can become 00304 * greater than "now". So, be sure to always evaluate a 00305 * coherent timer difference: 00306 * 00307 * 0 prev now TIMER_HW_CNT 00308 * |_____|_______________|_____| 00309 * ^^^^^^^^^^^^^^^ 00310 * delta = now - prev 00311 * 00312 * 0 now prev TIMER_HW_CNT 00313 * |_____|_______________|_____| 00314 * ^^^^^ ^^^^^ 00315 * delta = (TIMER_HW_CNT - prev) + now 00316 * 00317 * NOTE: TIMER_HW_CNT can be any value, not necessarily a power 00318 * of 2. For this reason the "%" operator is not suitable for 00319 * the generic case. 00320 */ 00321 delta = (now < prev) ? ((hptime_t)TIMER_HW_CNT - prev + now) : 00322 (now - prev); 00323 if (delta >= delay) 00324 break; 00325 delay -= delta; 00326 prev = now; 00327 } 00328 } 00329 00337 void timer_delayHp(hptime_t delay) 00338 { 00339 if (UNLIKELY(delay > us_to_hptime(1000))) 00340 { 00341 timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC)); 00342 delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC); 00343 } 00344 00345 timer_busyWait(delay); 00346 } 00347 #endif /* CONFIG_TIMER_UDELAY */ 00348 00353 DEFINE_TIMER_ISR 00354 { 00355 /* 00356 * With the Metrowerks compiler, the only way to force the compiler generate 00357 * an interrupt service routine is to put a pragma directive within the function 00358 * body. 00359 */ 00360 #ifdef __MWERKS__ 00361 #pragma interrupt saveall 00362 #endif 00363 00364 /* 00365 * On systems sharing IRQ line and vector, this check is needed 00366 * to ensure that IRQ is generated by timer source. 00367 */ 00368 if (!timer_hw_triggered()) 00369 return; 00370 00371 TIMER_STROBE_ON; 00372 00373 /* Update the master ms counter */ 00374 ++_clock; 00375 00376 /* Update the current task's quantum (if enabled). */ 00377 proc_decQuantum(); 00378 00379 #if CONFIG_TIMER_EVENTS 00380 timer_poll(&timers_queue); 00381 #endif 00382 00383 /* Perform hw IRQ handling */ 00384 timer_hw_irq(); 00385 00386 TIMER_STROBE_OFF; 00387 } 00388 00389 MOD_DEFINE(timer) 00390 00391 00394 void timer_init(void) 00395 { 00396 #if CONFIG_KERN_IRQ 00397 MOD_CHECK(irq); 00398 #endif 00399 00400 #if CONFIG_TIMER_EVENTS 00401 LIST_INIT(&timers_queue); 00402 #endif 00403 00404 TIMER_STROBE_INIT; 00405 00406 _clock = 0; 00407 00408 timer_hw_init(); 00409 00410 MOD_INIT(timer); 00411 } 00412 00413 00414 #if (ARCH & ARCH_EMUL) || (CPU_ARM_AT91) 00415 00418 void timer_cleanup(void) 00419 { 00420 MOD_CLEANUP(timer); 00421 00422 timer_hw_cleanup(); 00423 } 00424 #endif
![(please configure the [header_logo] section in trac.ini)](/chrome/site/bertos_logo.png)